# NetCop AI Hub - Django Recreation Guide ## Overview This guide provides complete instructions to recreate the NetCop AI Hub application using Django, reducing complexity from **7/10 to 3/10** while maintaining all functionality. **Current Next.js App**: 44 files, complex state management, custom authentication **Target Django App**: ~15 files, built-in features, simplified architecture ## 🎯 Key Benefits of Django Version ### Simplicity Gains - **Built-in Admin Panel**: No need to build user management UI - **Built-in Authentication**: No custom auth system needed - **ORM**: Automatic database handling vs manual SQL - **Templates**: Server-side rendering vs complex client state - **Single Language**: Python only vs JavaScript + TypeScript - **Built-in Security**: CSRF, XSS protection included ### Functionality Preserved - ✅ All 6 AI agents (Data Analyzer, Weather, 5 Whys, FAQ, Social Ads, Job Posting) - ✅ Wallet system with AED pricing - ✅ Stripe payment integration - ✅ User authentication & profiles - ✅ Agent marketplace - ✅ Transaction history - ✅ N8N workflow integration - ✅ File upload capabilities ## 🏗️ Project Structure ``` netcop_django/ ├── manage.py ├── requirements.txt ├── netcop_hub/ │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── apps/ │ ├── authentication/ │ │ ├── models.py │ │ ├── views.py │ │ └── urls.py │ ├── agents/ │ │ ├── models.py │ │ ├── views.py │ │ ├── urls.py │ │ └── agent_processors.py │ ├── wallet/ │ │ ├── models.py │ │ ├── views.py │ │ ├── urls.py │ │ └── stripe_handler.py │ └── core/ │ ├── models.py │ ├── views.py │ └── urls.py ├── templates/ │ ├── base.html │ ├── marketplace.html │ ├── agent_detail.html │ ├── pricing.html │ └── profile.html ├── static/ │ ├── css/ │ ├── js/ │ └── img/ └── media/ └── uploads/ ``` ## 🔥 Critical Missing Components Analysis After thorough review of the current Next.js app, here are the missing components that MUST be added to the Django version: ### 📱 **Missing Pages** 1. **Homepage (/)** - Complex landing page with animations, hero sections, client testimonials 2. **Debug Page (/debug)** - Environment variable debugging tool 3. **Password Reset (/reset-password)** - Complete password reset functionality with Supabase integration ### 🧩 **Missing Shared Components** 4. **Header Component** - Complex responsive header with mobile menu, wallet balance, user dropdown 5. **Footer Component** - Company information and links 6. **AuthModal** - Login/register modal system 7. **ProfileModal** - User profile dropdown with settings ### 🤖 **Missing Agent Components** 8. **AgentLayout** - Shared layout wrapper for all agent pages 9. **ProcessingStatus** - Animated processing feedback 10. **ResultsDisplay** - Enhanced results with copy/download functionality 11. **FileUpload** - Drag & drop file upload with validation 12. **Advanced Chat Interface** - 5 Whys agent has sophisticated chat UI with markdown rendering ### 🎨 **Missing UI/UX Features** 13. **Design System** - Centralized colors, spacing, typography (`/src/lib/designSystem.ts`) 14. **Style Utilities** - Animation helpers, button styles (`/src/lib/styleUtils.ts`) 15. **Glassmorphism Effects** - Backdrop blur, transparency layers 16. **Mobile Responsiveness** - Extensive mobile optimizations with clamp(), touch targets 17. **Real-time Animations** - Scroll-triggered effects, hover interactions 18. **Wallet Status Indicators** - Color-coded balance warnings and pulsing animations ### 🔐 **Missing Utility Systems** 19. **Environment Validation** - Client-side environment checking 20. **Input Validation** - Comprehensive sanitization 21. **Wallet Utilities** - Balance formatting, status calculation 22. **Advanced Error Handling** - User-friendly error boundaries ## 🚀 Step-by-Step Implementation ### Step 1: Project Setup ```bash # Create new Django project mkdir netcop_django cd netcop_django # Create virtual environment python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate # Install dependencies pip install django djangorestframework stripe python-decouple requests pillow pip install psycopg2-binary # for PostgreSQL (or sqlite3 for development) # Create project django-admin startproject netcop_hub . # Create apps python manage.py startapp authentication python manage.py startapp agents python manage.py startapp wallet python manage.py startapp core ``` ### Step 2: Database Models #### User Model (authentication/models.py) ```python from django.contrib.auth.models import AbstractUser from django.db import models from decimal import Decimal class User(AbstractUser): email = models.EmailField(unique=True) wallet_balance = models.DecimalField(max_digits=10, decimal_places=2, default=Decimal('0.00')) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['username'] def __str__(self): return self.email def has_sufficient_balance(self, amount): return self.wallet_balance >= Decimal(str(amount)) def deduct_balance(self, amount, description="", agent_slug=""): if self.has_sufficient_balance(amount): self.wallet_balance -= Decimal(str(amount)) self.save() # Create transaction record WalletTransaction.objects.create( user=self, amount=-Decimal(str(amount)), type='agent_usage', description=description, agent_slug=agent_slug ) return True return False def add_balance(self, amount, description="", stripe_session_id=""): self.wallet_balance += Decimal(str(amount)) self.save() # Create transaction record WalletTransaction.objects.create( user=self, amount=Decimal(str(amount)), type='top_up', description=description, stripe_session_id=stripe_session_id ) ``` #### Wallet Transaction Model (wallet/models.py) ```python from django.db import models from django.contrib.auth import get_user_model import uuid User = get_user_model() class WalletTransaction(models.Model): TRANSACTION_TYPES = [ ('top_up', 'Top Up'), ('agent_usage', 'Agent Usage'), ('refund', 'Refund'), ] id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='wallet_transactions') amount = models.DecimalField(max_digits=10, decimal_places=2) type = models.CharField(max_length=20, choices=TRANSACTION_TYPES) description = models.TextField() agent_slug = models.CharField(max_length=100, blank=True) stripe_session_id = models.CharField(max_length=200, blank=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: ordering = ['-created_at'] def __str__(self): return f"{self.user.email} - {self.amount} AED ({self.type})" ``` #### Agent Model (agents/models.py) ```python from django.db import models from decimal import Decimal class Agent(models.Model): CATEGORIES = [ ('analytics', 'Analytics'), ('utilities', 'Utilities'), ('content', 'Content'), ('marketing', 'Marketing'), ('customer-service', 'Customer Service'), ] name = models.CharField(max_length=200) slug = models.SlugField(unique=True) description = models.TextField() category = models.CharField(max_length=50, choices=CATEGORIES) price = models.DecimalField(max_digits=10, decimal_places=2) icon = models.CharField(max_length=10, default='🤖') is_active = models.BooleanField(default=True) rating = models.DecimalField(max_digits=3, decimal_places=1, default=Decimal('4.5')) review_count = models.IntegerField(default=0) n8n_webhook_url = models.URLField(blank=True) created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return self.name @property def price_display(self): return f"{self.price} AED" def get_gradient_class(self): gradient_map = { 'analytics': 'from-indigo-500 to-purple-600', 'utilities': 'from-sky-400 to-blue-500', 'content': 'from-purple-500 to-indigo-600', 'marketing': 'from-pink-500 to-rose-600', 'customer-service': 'from-blue-500 to-blue-600', } return gradient_map.get(self.category, 'from-gray-500 to-gray-600') ``` ### Step 3: Agent Processing System #### Agent Processors (agents/agent_processors.py) ```python import requests from django.conf import settings from django.core.files.storage import default_storage from django.core.files.base import ContentFile import json import os class AgentProcessor: def __init__(self, agent_slug): self.agent_slug = agent_slug self.webhook_urls = { 'data-analyzer': settings.N8N_WEBHOOK_DATA_ANALYZER, 'five-whys': settings.N8N_WEBHOOK_FIVE_WHYS, 'job-posting-generator': settings.N8N_WEBHOOK_JOB_POSTING, 'faq-generator': settings.N8N_WEBHOOK_FAQ_GENERATOR, 'social-ads-generator': settings.N8N_WEBHOOK_SOCIAL_ADS, 'weather-reporter': settings.OPENWEATHER_API_KEY, } def process_data_analyzer(self, file_obj, user_id): """Process file through N8N data analyzer webhook""" webhook_url = self.webhook_urls.get('data-analyzer') if not webhook_url: raise ValueError("Data analyzer webhook URL not configured") files = {'file': file_obj} data = {'userId': user_id} response = requests.post(webhook_url, files=files, data=data, timeout=60) response.raise_for_status() return response.json() def process_five_whys(self, problem_description, user_id): """Process 5 whys analysis through N8N""" webhook_url = self.webhook_urls.get('five-whys') if not webhook_url: raise ValueError("Five whys webhook URL not configured") data = { 'problem': problem_description, 'userId': user_id } response = requests.post(webhook_url, json=data, timeout=60) response.raise_for_status() return response.json() def process_weather_reporter(self, location): """Get weather data using OpenWeather API""" api_key = settings.OPENWEATHER_API_KEY if not api_key: raise ValueError("OpenWeather API key not configured") url = f"https://api.openweathermap.org/data/2.5/weather" params = { 'q': location, 'appid': api_key, 'units': 'metric' } response = requests.get(url, params=params, timeout=30) response.raise_for_status() return response.json() def process_job_posting(self, job_details, user_id): """Generate job posting through N8N""" webhook_url = self.webhook_urls.get('job-posting-generator') if not webhook_url: raise ValueError("Job posting webhook URL not configured") data = { 'jobDetails': job_details, 'userId': user_id } response = requests.post(webhook_url, json=data, timeout=60) response.raise_for_status() return response.json() def process_social_ads(self, ad_requirements, user_id): """Generate social ads through N8N""" webhook_url = self.webhook_urls.get('social-ads-generator') if not webhook_url: raise ValueError("Social ads webhook URL not configured") data = { 'adRequirements': ad_requirements, 'userId': user_id } response = requests.post(webhook_url, json=data, timeout=60) response.raise_for_status() return response.json() def process_faq_generator(self, content_source, user_id): """Generate FAQ through N8N""" webhook_url = self.webhook_urls.get('faq-generator') if not webhook_url: raise ValueError("FAQ generator webhook URL not configured") data = { 'contentSource': content_source, 'userId': user_id } response = requests.post(webhook_url, json=data, timeout=60) response.raise_for_status() return response.json() def process_agent(self, **kwargs): """Main processing method - routes to appropriate processor""" processor_map = { 'data-analyzer': self.process_data_analyzer, 'five-whys': self.process_five_whys, 'weather-reporter': self.process_weather_reporter, 'job-posting-generator': self.process_job_posting, 'social-ads-generator': self.process_social_ads, 'faq-generator': self.process_faq_generator, } processor = processor_map.get(self.agent_slug) if not processor: raise ValueError(f"No processor found for agent: {self.agent_slug}") return processor(**kwargs) ``` ### Step 4: Views #### Complete Views with All Missing Functionality (core/views.py) ```python from django.shortcuts import render, redirect, get_object_or_404 from django.contrib.auth.decorators import login_required from django.contrib import messages from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_http_methods from django.conf import settings from django.contrib.auth import get_user_model from agents.models import Agent from agents.agent_processors import AgentProcessor import json import os User = get_user_model() def homepage(request): """Enhanced homepage with all features from Next.js version""" # Get featured agents for preview featured_agents = Agent.objects.filter(is_active=True)[:3] context = { 'featured_agents': featured_agents, 'total_agents': Agent.objects.filter(is_active=True).count(), 'total_users': User.objects.count(), } return render(request, 'homepage.html', context) def marketplace(request): """Display all available agents with enhanced filtering""" category_filter = request.GET.get('category') search_query = request.GET.get('search') agents = Agent.objects.filter(is_active=True) if category_filter: agents = agents.filter(category=category_filter) if search_query: agents = agents.filter( models.Q(name__icontains=search_query) | models.Q(description__icontains=search_query) ) # Get unique categories for filter dropdown categories = Agent.objects.filter(is_active=True).values_list('category', flat=True).distinct() context = { 'agents': agents.order_by('category', 'name'), 'categories': categories, 'current_category': category_filter, 'search_query': search_query, } return render(request, 'marketplace.html', context) def pricing(request): """Enhanced pricing page with payment status handling""" packages = [ { 'id': 'basic', 'amount': 10, 'price': 9.99, 'label': 'Basic', 'description': 'Perfect for trying out AI agents', 'features': ['2-4 agent uses', 'Basic support', 'Email notifications'], 'icon': '💰', 'gradient': 'from-blue-500 to-purple-600' }, { 'id': 'popular', 'amount': 50, 'price': 49.99, 'label': 'Popular', 'description': 'Most popular choice for regular users', 'features': ['10-25 agent uses', 'Priority support', 'Advanced analytics', 'Export options'], 'icon': '⭐', 'gradient': 'from-purple-500 to-pink-600', 'popular': True }, { 'id': 'premium', 'amount': 100, 'price': 99.99, 'label': 'Premium', 'description': 'For power users and small teams', 'features': ['50+ agent uses', '24/7 support', 'Custom integrations', 'Team collaboration'], 'icon': '🚀', 'gradient': 'from-green-500 to-teal-600' }, { 'id': 'enterprise', 'amount': 500, 'price': 499.99, 'label': 'Enterprise', 'description': 'For large teams and businesses', 'features': ['Unlimited uses', 'Dedicated support', 'Custom development', 'SLA guarantee'], 'icon': '👑', 'gradient': 'from-yellow-500 to-red-600' }, ] # Handle payment status messages (prevent duplicate messages) payment_status = request.GET.get('payment') session_id = request.GET.get('session_id') # Create session key to prevent duplicate messages if payment_status: session_key = f"payment_message_{payment_status}_{session_id or 'cancelled'}" if not request.session.get(session_key): request.session[session_key] = True if payment_status == 'success': messages.success(request, '✅ Payment successful! Your wallet has been topped up.') elif payment_status == 'cancelled': messages.error(request, '❌ Payment was cancelled. No charges were made.') # FAQ data faqs = [ { 'question': 'How does the pay-per-use pricing work?', 'answer': 'You add money to your wallet and pay for each AI agent use. Prices range from 2.00 to 8.00 AED per use.' }, { 'question': 'Do wallet funds expire?', 'answer': 'No, your wallet balance never expires. Use it whenever you need AI assistance.' }, { 'question': 'Can I get a refund?', 'answer': 'Yes, unused wallet balance can be refunded within 30 days of purchase.' }, { 'question': 'Is my payment information secure?', 'answer': 'Absolutely. We use Stripe for secure payment processing and never store your payment details.' } ] context = { 'packages': packages, 'faqs': faqs, } return render(request, 'pricing.html', context) def debug_page(request): """Debug page for development environment checking""" if not settings.DEBUG: context = {'debug_mode': False} return render(request, 'debug.html', context) # Environment status check env_status = { 'DATABASE_URL': bool(os.getenv('DATABASE_URL')), 'STRIPE_SECRET_KEY': bool(settings.STRIPE_SECRET_KEY), 'N8N_WEBHOOK_DATA_ANALYZER': bool(settings.N8N_WEBHOOK_DATA_ANALYZER), 'N8N_WEBHOOK_FIVE_WHYS': bool(settings.N8N_WEBHOOK_FIVE_WHYS), 'OPENWEATHER_API_KEY': bool(settings.OPENWEATHER_API_KEY), 'DEBUG': settings.DEBUG, 'ALLOWED_HOSTS': settings.ALLOWED_HOSTS, } # Database connection test try: user_count = User.objects.count() agent_count = Agent.objects.count() db_status = {'status': 'Connected', 'color': 'green'} except Exception as e: user_count = 0 agent_count = 0 db_status = {'status': f'Error: {str(e)}', 'color': 'red'} context = { 'debug_mode': True, 'env_status': json.dumps(env_status, indent=2), 'db_status': db_status, 'user_count': user_count, 'agent_count': agent_count, } return render(request, 'debug.html', context) def reset_password(request): """Password reset functionality""" if request.method == 'POST': password = request.POST.get('password') confirm_password = request.POST.get('confirm_password') if not password or not confirm_password: context = {'error': 'Both password fields are required'} return render(request, 'reset_password.html', context) if password != confirm_password: context = {'error': 'Passwords do not match'} return render(request, 'reset_password.html', context) if len(password) < 8: context = {'error': 'Password must be at least 8 characters long'} return render(request, 'reset_password.html', context) # In a real implementation, you would: # 1. Verify the reset token from the URL # 2. Update the user's password # 3. Redirect to login with success message messages.success(request, 'Password updated successfully! Please log in with your new password.') return redirect('homepage') # Check if we have a valid reset token (simplified version) token = request.GET.get('token') if not token: context = {'error': 'Invalid or expired reset link. Please request a new password reset.'} return render(request, 'reset_password.html', context) return render(request, 'reset_password.html') @login_required def agent_detail(request, slug): """Enhanced agent detail page with wallet balance checking""" agent = get_object_or_404(Agent, slug=slug, is_active=True) # Calculate wallet status user_balance = request.user.wallet_balance has_sufficient_balance = user_balance >= agent.price # Calculate usage count if has_sufficient_balance: possible_uses = int(user_balance / agent.price) else: possible_uses = 0 context = { 'agent': agent, 'user_balance': user_balance, 'has_sufficient_balance': has_sufficient_balance, 'possible_uses': possible_uses, 'balance_after_use': user_balance - agent.price if has_sufficient_balance else user_balance, } return render(request, 'agent_detail.html', context) @login_required @require_http_methods(["POST"]) def process_agent(request, slug): """Enhanced agent processing with comprehensive error handling""" agent = get_object_or_404(Agent, slug=slug, is_active=True) # Check wallet balance if not request.user.has_sufficient_balance(agent.price): return JsonResponse({ 'success': False, 'error': f'Insufficient balance. Required: {agent.price_display}, Available: {request.user.wallet_balance:.2f} AED' }, status=400) try: # Process based on agent type processor = AgentProcessor(agent.slug) if agent.slug == 'data-analyzer': file_obj = request.FILES.get('file') if not file_obj: return JsonResponse({'success': False, 'error': 'File is required'}, status=400) # Validate file size (10MB limit) if file_obj.size > 10 * 1024 * 1024: return JsonResponse({'success': False, 'error': 'File size must be less than 10MB'}, status=400) # Validate file type allowed_extensions = ['.csv', '.xlsx', '.xls', '.json'] file_extension = os.path.splitext(file_obj.name)[1].lower() if file_extension not in allowed_extensions: return JsonResponse({'success': False, 'error': 'Invalid file type. Allowed: CSV, Excel, JSON'}, status=400) result = processor.process_agent(file_obj=file_obj, user_id=str(request.user.id)) elif agent.slug == 'five-whys': problem = request.POST.get('problem') if not problem or len(problem.strip()) < 10: return JsonResponse({'success': False, 'error': 'Problem description must be at least 10 characters'}, status=400) result = processor.process_agent(problem_description=problem, user_id=str(request.user.id)) elif agent.slug == 'weather-reporter': location = request.POST.get('location') if not location or len(location.strip()) < 2: return JsonResponse({'success': False, 'error': 'Location must be at least 2 characters'}, status=400) result = processor.process_agent(location=location) elif agent.slug == 'job-posting-generator': required_fields = ['title', 'company', 'description', 'requirements'] job_details = {} for field in required_fields: value = request.POST.get(field, '').strip() if not value: return JsonResponse({'success': False, 'error': f'{field.title()} is required'}, status=400) if len(value) < 5: return JsonResponse({'success': False, 'error': f'{field.title()} must be at least 5 characters'}, status=400) job_details[field] = value result = processor.process_agent(job_details=job_details, user_id=str(request.user.id)) elif agent.slug == 'social-ads-generator': required_fields = ['product', 'platform', 'target_audience', 'tone'] ad_requirements = {} for field in required_fields: value = request.POST.get(field, '').strip() if not value: return JsonResponse({'success': False, 'error': f'{field.replace("_", " ").title()} is required'}, status=400) ad_requirements[field] = value # Validate platform valid_platforms = ['facebook', 'instagram', 'twitter', 'linkedin'] if ad_requirements['platform'] not in valid_platforms: return JsonResponse({'success': False, 'error': 'Invalid platform selected'}, status=400) # Validate tone valid_tones = ['professional', 'casual', 'humorous', 'urgent'] if ad_requirements['tone'] not in valid_tones: return JsonResponse({'success': False, 'error': 'Invalid tone selected'}, status=400) result = processor.process_agent(ad_requirements=ad_requirements, user_id=str(request.user.id)) elif agent.slug == 'faq-generator': content_source = request.POST.get('content_source', '').strip() if not content_source: return JsonResponse({'success': False, 'error': 'Content source is required'}, status=400) if len(content_source) < 50: return JsonResponse({'success': False, 'error': 'Content source must be at least 50 characters'}, status=400) result = processor.process_agent(content_source=content_source, user_id=str(request.user.id)) else: return JsonResponse({'success': False, 'error': 'Agent not supported'}, status=400) # Deduct balance on successful processing if request.user.deduct_balance( agent.price, f"Used {agent.name}", agent.slug ): return JsonResponse({ 'success': True, 'result': result, 'new_balance': float(request.user.wallet_balance), 'agent_used': agent.name, 'cost': float(agent.price) }) else: return JsonResponse({ 'success': False, 'error': 'Failed to process payment. Please try again.' }, status=400) except Exception as e: # Log the error in production if not settings.DEBUG: import logging logger = logging.getLogger(__name__) logger.error(f"Agent processing error: {str(e)}", exc_info=True) return JsonResponse({ 'success': False, 'error': 'An error occurred while processing your request. Please try again later.' }, status=500) @login_required def profile(request): """Enhanced user profile with transaction history and wallet management""" # Get recent transactions transactions = request.user.wallet_transactions.all()[:50] # Last 50 transactions # Calculate usage statistics total_spent = sum(abs(t.amount) for t in transactions if t.type == 'agent_usage') total_topped_up = sum(t.amount for t in transactions if t.type == 'top_up') total_agents_used = transactions.filter(type='agent_usage').count() # Get most used agents from django.db.models import Count popular_agents = (transactions.filter(type='agent_usage') .values('agent_slug') .annotate(count=Count('agent_slug')) .order_by('-count')[:5]) # Wallet status balance = request.user.wallet_balance if balance < 5: wallet_status = {'status': 'low', 'color': 'red', 'message': 'Low balance - Add money to continue using agents'} elif balance < 20: wallet_status = {'status': 'medium', 'color': 'orange', 'message': 'Consider adding more funds'} else: wallet_status = {'status': 'high', 'color': 'green', 'message': 'Good balance'} context = { 'transactions': transactions, 'total_spent': total_spent, 'total_topped_up': total_topped_up, 'total_agents_used': total_agents_used, 'popular_agents': popular_agents, 'wallet_status': wallet_status, } return render(request, 'profile.html', context) # API endpoints for AJAX functionality @login_required @require_http_methods(["GET"]) def check_wallet_balance(request): """API endpoint to check current wallet balance""" return JsonResponse({ 'balance': float(request.user.wallet_balance), 'formatted_balance': f"{request.user.wallet_balance:.2f} AED" }) @login_required @require_http_methods(["POST"]) def chat_message(request, slug): """Handle chat messages for interactive agents like 5 Whys""" if slug != 'five-whys': return JsonResponse({'success': False, 'error': 'Chat not available for this agent'}, status=400) message = request.POST.get('message', '').strip() if not message: return JsonResponse({'success': False, 'error': 'Message is required'}, status=400) # Here you would integrate with your 5 Whys processing logic # For now, return a simple response response_message = f"Thank you for: {message}. Let me ask you the next Why question..." return JsonResponse({ 'success': True, 'response': response_message, 'timestamp': timezone.now().isoformat() }) #### Main Views (core/views.py) ```python from django.shortcuts import render, redirect, get_object_or_404 from django.contrib.auth.decorators import login_required from django.contrib import messages from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_http_methods from agents.models import Agent from agents.agent_processors import AgentProcessor import json def marketplace(request): """Display all available agents""" agents = Agent.objects.filter(is_active=True).order_by('category', 'name') return render(request, 'marketplace.html', {'agents': agents}) def pricing(request): """Display pricing packages""" packages = [ {'id': 'basic', 'amount': 10, 'price': 9.99, 'label': 'Basic'}, {'id': 'popular', 'amount': 50, 'price': 49.99, 'label': 'Popular'}, {'id': 'premium', 'amount': 100, 'price': 99.99, 'label': 'Premium'}, {'id': 'enterprise', 'amount': 500, 'price': 499.99, 'label': 'Enterprise'}, ] # Handle payment status messages payment_status = request.GET.get('payment') if payment_status == 'success': messages.success(request, '✅ Payment successful! Your wallet has been topped up.') elif payment_status == 'cancelled': messages.error(request, '❌ Payment was cancelled. No charges were made.') return render(request, 'pricing.html', {'packages': packages}) @login_required def agent_detail(request, slug): """Display agent detail page and handle processing""" agent = get_object_or_404(Agent, slug=slug, is_active=True) context = { 'agent': agent, 'user_balance': request.user.wallet_balance, 'has_sufficient_balance': request.user.has_sufficient_balance(agent.price) } return render(request, 'agent_detail.html', context) @login_required @require_http_methods(["POST"]) def process_agent(request, slug): """Process agent request""" agent = get_object_or_404(Agent, slug=slug, is_active=True) # Check wallet balance if not request.user.has_sufficient_balance(agent.price): return JsonResponse({ 'success': False, 'error': f'Insufficient balance. Required: {agent.price_display}' }, status=400) try: # Process based on agent type processor = AgentProcessor(agent.slug) if agent.slug == 'data-analyzer': file_obj = request.FILES.get('file') if not file_obj: return JsonResponse({'success': False, 'error': 'File is required'}, status=400) result = processor.process_agent(file_obj=file_obj, user_id=str(request.user.id)) elif agent.slug == 'five-whys': problem = request.POST.get('problem') if not problem: return JsonResponse({'success': False, 'error': 'Problem description is required'}, status=400) result = processor.process_agent(problem_description=problem, user_id=str(request.user.id)) elif agent.slug == 'weather-reporter': location = request.POST.get('location') if not location: return JsonResponse({'success': False, 'error': 'Location is required'}, status=400) result = processor.process_agent(location=location) elif agent.slug == 'job-posting-generator': job_details = { 'title': request.POST.get('title'), 'company': request.POST.get('company'), 'description': request.POST.get('description'), 'requirements': request.POST.get('requirements'), } result = processor.process_agent(job_details=job_details, user_id=str(request.user.id)) elif agent.slug == 'social-ads-generator': ad_requirements = { 'product': request.POST.get('product'), 'platform': request.POST.get('platform'), 'target_audience': request.POST.get('target_audience'), 'tone': request.POST.get('tone'), } result = processor.process_agent(ad_requirements=ad_requirements, user_id=str(request.user.id)) elif agent.slug == 'faq-generator': content_source = request.POST.get('content_source') if not content_source: return JsonResponse({'success': False, 'error': 'Content source is required'}, status=400) result = processor.process_agent(content_source=content_source, user_id=str(request.user.id)) else: return JsonResponse({'success': False, 'error': 'Agent not supported'}, status=400) # Deduct balance on successful processing if request.user.deduct_balance( agent.price, f"Used {agent.name}", agent.slug ): return JsonResponse({ 'success': True, 'result': result, 'new_balance': float(request.user.wallet_balance) }) else: return JsonResponse({ 'success': False, 'error': 'Failed to process payment' }, status=400) except Exception as e: return JsonResponse({ 'success': False, 'error': str(e) }, status=500) @login_required def profile(request): """User profile with transaction history""" transactions = request.user.wallet_transactions.all()[:20] # Last 20 transactions return render(request, 'profile.html', {'transactions': transactions}) ``` #### Stripe Integration (wallet/stripe_handler.py) ```python import stripe from django.conf import settings from django.http import JsonResponse, HttpResponse from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_http_methods from django.shortcuts import redirect from django.contrib.auth import get_user_model from django.contrib import messages import json User = get_user_model() stripe.api_key = settings.STRIPE_SECRET_KEY @require_http_methods(["GET"]) def create_checkout_session(request): """Create Stripe checkout session for wallet top-up""" package_id = request.GET.get('package') user_id = request.GET.get('user') success_url = request.GET.get('success') cancel_url = request.GET.get('cancel') if not all([package_id, user_id, success_url, cancel_url]): return JsonResponse({'error': 'Missing required parameters'}, status=400) # Package pricing packages = { 'basic': {'amount': 999, 'currency': 'aed', 'name': 'Basic Package - 10 AED'}, 'popular': {'amount': 4999, 'currency': 'aed', 'name': 'Popular Package - 50 AED'}, 'premium': {'amount': 9999, 'currency': 'aed', 'name': 'Premium Package - 100 AED'}, 'enterprise': {'amount': 49999, 'currency': 'aed', 'name': 'Enterprise Package - 500 AED'}, } package = packages.get(package_id) if not package: return JsonResponse({'error': 'Invalid package'}, status=400) try: checkout_session = stripe.checkout.Session.create( payment_method_types=['card'], line_items=[{ 'price_data': { 'currency': package['currency'], 'product_data': { 'name': package['name'], }, 'unit_amount': package['amount'], }, 'quantity': 1, }], mode='payment', success_url=success_url, cancel_url=cancel_url, client_reference_id=user_id, metadata={ 'package_id': package_id, 'user_id': user_id, } ) return redirect(checkout_session.url) except Exception as e: return JsonResponse({'error': str(e)}, status=500) @csrf_exempt @require_http_methods(["POST"]) def stripe_webhook(request): """Handle Stripe webhook events""" payload = request.body sig_header = request.META.get('HTTP_STRIPE_SIGNATURE') try: event = stripe.Webhook.construct_event( payload, sig_header, settings.STRIPE_WEBHOOK_SECRET ) except ValueError: return HttpResponse(status=400) except stripe.error.SignatureVerificationError: return HttpResponse(status=400) # Handle successful payment if event['type'] == 'checkout.session.completed': session = event['data']['object'] # Get user and package info user_id = session['client_reference_id'] package_id = session['metadata']['package_id'] try: user = User.objects.get(id=user_id) # Add balance based on package package_amounts = { 'basic': 10, 'popular': 50, 'premium': 100, 'enterprise': 500, } amount = package_amounts.get(package_id, 0) if amount > 0: user.add_balance( amount, f"Wallet top-up: {amount} AED", session['id'] ) except User.DoesNotExist: pass return HttpResponse(status=200) ``` ### Step 5: Templates #### Base Template (templates/base.html) ```html
Discover powerful AI agents that automate your workflows, analyze data, and boost productivity. Pay per use with transparent AED pricing.
Our AI agents are designed to solve real business problems with transparent pricing and proven results.
Get immediate insights and results from our powerful AI agents. No waiting, no delays.
Pay only for what you use. Clear AED pricing with no hidden fees or subscriptions.
Enterprise-grade security with 99.9% uptime. Your data is safe and protected.
Discover our most powerful AI agents for business automation
Join hundreds of businesses already using NetCop AI Hub to automate their workflows
This debug page is only available in development mode.
{% else %}{{ env_status|safe }}
Database Connection: {{ db_status.status }}
User Count: {{ user_count }}
Agent Count: {{ agent_count }}
Enter your new password below
Choose from our collection of powerful AI agents
{{ agent.description }}
{{ agent.description }}
👋 Welcome to the 5 Whys Root Cause Analysis!
I'll help you systematically analyze your problem using the proven 5 Whys methodology.
To get started, please describe the problem you're experiencing.
For example:
What problem would you like to analyze?
Drop your file here or click to browse
Supports CSV, Excel, JSON files up to 10MB
Top up wallet to use this agent
{% endif %}{{ agent.description }}
Top up wallet to use this agent
{% endif %}