import stripe from django.conf import settings from django.contrib.auth import get_user_model from django.http import JsonResponse from decimal import Decimal import json import time User = get_user_model() stripe.api_key = settings.STRIPE_SECRET_KEY # โœ… CRITICAL: Set API version to match webhook configuration stripe.api_version = "2025-05-28.basil" class StripePaymentHandler: def __init__(self): self.allowed_amounts = [10, 50, 100, 500] def create_checkout_session(self, user, amount, request=None): """Create a Stripe checkout session for wallet top-up (Modern Integration)""" if amount not in self.allowed_amounts: raise ValueError(f"Invalid amount: {amount}. Allowed amounts: {self.allowed_amounts}") # Build URLs with session_id parameter for payment verification if request: success_url = request.build_absolute_uri('/wallet/top-up/success/') + '?session_id={CHECKOUT_SESSION_ID}' cancel_url = request.build_absolute_uri('/wallet/top-up/cancel/') else: # Fallback URLs success_url = 'https://quantumtaskai.com/wallet/top-up/success/?session_id={CHECKOUT_SESSION_ID}' cancel_url = 'https://quantumtaskai.com/wallet/top-up/cancel/' try: print(f"๐Ÿš€ [STRIPE DEBUG] Starting checkout session creation...") print(f"๐Ÿ‘ค User: {user.id} ({user.email})") print(f"๐Ÿ’ฐ Amount: {amount} AED") print(f"๐Ÿ”‘ Stripe API Key configured: {bool(settings.STRIPE_SECRET_KEY)}") print(f"๐Ÿ”‘ API Version: {stripe.api_version}") print(f"๐Ÿ“ Success URL: {success_url}") print(f"๐Ÿ“ Cancel URL: {cancel_url}") print(f"๐Ÿ“ Expected Webhook URL: https://quantumtaskai.com/stripe/webhook/") print(f"๐ŸŒ Environment: {'production' if 'railway.app' in (request.get_host() if request else '') else 'development'}") # Create session with modern Stripe practices session = stripe.checkout.Session.create( # Modern payment method configuration payment_method_types=['card'], # Line items configuration line_items=[{ 'price_data': { 'currency': 'aed', 'product_data': { 'name': 'Quantum Tasks AI Wallet Top-up', 'description': f'Add {amount} AED to your wallet balance', 'metadata': { 'service': 'quantumtaskai_wallet', 'user_id': str(user.id) } }, 'unit_amount': int(amount * 100), # Convert to fils (AED cents) }, 'quantity': 1, }], # Payment configuration mode='payment', # URLs with session ID parameter success_url=success_url, cancel_url=cancel_url, # Customer and reference data client_reference_id=str(user.id), customer_email=user.email, # Comprehensive metadata for webhook processing metadata={ 'user_id': str(user.id), 'user_email': user.email, 'amount': str(amount), 'currency': 'aed', 'type': 'wallet_topup', 'service': 'quantumtaskai', 'environment': 'production' if 'railway.app' in (request.get_host() if request else '') else 'development', 'created_at': str(int(time.time())), 'app_version': '1.0' }, # Modern Stripe features payment_intent_data={ 'metadata': { 'user_id': str(user.id), 'amount': str(amount), 'service': 'quantumtaskai_wallet' } }, # Automatic tax and billing automatic_tax={'enabled': False}, # Expiration expires_at=int(time.time()) + (30 * 60), # 30 minutes from now ) print(f"โœ… [STRIPE DEBUG] Session created successfully!") print(f" ๐Ÿ’ณ Session ID: {session.id}") print(f" ๐Ÿ‘ค Client Reference: {session.client_reference_id}") print(f" ๐Ÿ‘ค Customer Email: {session.customer_email}") print(f" ๐Ÿ’ฐ Amount Total: {session.amount_total} fils ({session.amount_total / 100} AED)") print(f" ๐Ÿ’ฑ Currency: {session.currency}") print(f" ๐Ÿ”— Payment URL: {session.url}") print(f" ๐Ÿ“Š Status: {session.status}") print(f" ๐Ÿ’ณ Payment Status: {session.payment_status}") print(f" โฐ Created: {session.created}") print(f" โฐ Expires: {session.expires_at}") print(f" ๐Ÿท๏ธ Mode: {session.mode}") print(f" ๐Ÿ†” Object Type: {session.object}") print(f" ๐Ÿ“ Metadata: {session.metadata}") # CRITICAL: Verify session was created in correct Stripe account print(f"๐Ÿ” [STRIPE DEBUG] Verifying session exists immediately...") try: verification_session = stripe.checkout.Session.retrieve(session.id) print(f"โœ… [STRIPE DEBUG] Session verification successful!") print(f" ๐Ÿ”— Retrieved Session ID: {verification_session.id}") print(f" ๐Ÿ“Š Retrieved Status: {verification_session.status}") print(f" ๐Ÿ‘ค Retrieved Customer Email: {verification_session.customer_email}") except Exception as verify_error: print(f"โŒ [STRIPE DEBUG] Session verification FAILED: {verify_error}") print(f"โŒ This means the session was NOT created in the expected Stripe account!") return { 'payment_url': session.url, 'session_id': session.id, 'amount': amount, 'currency': 'aed', 'expires_at': session.expires_at } except stripe.error.StripeError as e: print(f"โŒ [MODERN] Stripe error: {str(e)}") raise ValueError(f"Failed to create checkout session: {str(e)}") def verify_payment(self, session_id): """Verify payment directly from Stripe (bypasses webhook issues)""" try: print(f"๐Ÿ” [STRIPE DEBUG] Starting payment verification...") print(f"๐Ÿ”‘ Stripe API Key configured: {bool(settings.STRIPE_SECRET_KEY)}") print(f"๐Ÿ”‘ API Version: {stripe.api_version}") print(f"๐Ÿ’ณ Session ID to verify: {session_id}") session = stripe.checkout.Session.retrieve(session_id) print(f"โœ… [STRIPE DEBUG] Session retrieved successfully!") print(f" ๐Ÿ’ณ Session ID: {session.id}") print(f" ๐Ÿ“Š Session Status: {session.status}") print(f" ๐Ÿ’ณ Payment Status: {session.payment_status}") print(f" ๐Ÿ‘ค Client Reference ID: {session.client_reference_id}") print(f" ๐Ÿ‘ค Customer Email: {session.customer_email}") print(f" ๐Ÿ’ฐ Amount Total: {session.amount_total} fils ({session.amount_total / 100} AED)") print(f" ๐Ÿ’ฑ Currency: {session.currency}") print(f" โฐ Created: {session.created}") print(f" โฐ Expires At: {session.expires_at}") print(f" ๐Ÿท๏ธ Mode: {session.mode}") print(f" ๐Ÿ“ Metadata: {session.metadata}") print(f" ๐Ÿ’ณ Payment Intent: {getattr(session, 'payment_intent', 'None')}") print(f" ๐Ÿงพ Invoice: {getattr(session, 'invoice', 'None')}") print(f" ๐ŸŽฏ Success URL: {getattr(session, 'success_url', 'None')}") # Check if payment was actually completed if hasattr(session, 'payment_intent') and session.payment_intent: try: payment_intent = stripe.PaymentIntent.retrieve(session.payment_intent) print(f"๐Ÿ’ณ [STRIPE DEBUG] Payment Intent Details:") print(f" ๐Ÿ†” Payment Intent ID: {payment_intent.id}") print(f" ๐Ÿ“Š Status: {payment_intent.status}") print(f" ๐Ÿ’ฐ Amount: {payment_intent.amount} fils ({payment_intent.amount / 100} AED)") print(f" ๐Ÿ’ฑ Currency: {payment_intent.currency}") print(f" โฐ Created: {payment_intent.created}") print(f" ๐Ÿ“ Description: {payment_intent.description}") except Exception as pi_error: print(f"โŒ [STRIPE DEBUG] Could not retrieve Payment Intent: {pi_error}") if session.payment_status == 'paid' and session.status == 'complete': user_id = session.client_reference_id amount = session.amount_total / 100 # Convert from cents print(f"โœ… VERIFY: Payment successful - User: {user_id}, Amount: {amount}") # Process the payment manually (bypass webhook) if user_id: try: from django.contrib.auth import get_user_model User = get_user_model() user = User.objects.get(id=user_id) # Check if already processed to avoid double-charging from wallet.models import WalletTransaction existing = WalletTransaction.objects.filter(stripe_session_id=session_id).first() if not existing: print(f"๐Ÿ’ฐ VERIFY: Processing payment for {user.email}") user.add_balance( amount=amount, description=f"Wallet top-up via Stripe", stripe_session_id=session_id ) print(f"โœ… VERIFY: Balance updated successfully") return { 'success': True, 'amount': amount, 'user_id': user_id, 'processed': True, 'message': 'Payment processed via manual verification' } else: print(f"โš ๏ธ VERIFY: Payment already processed") return { 'success': True, 'amount': amount, 'user_id': user_id, 'processed': False, 'message': 'Payment already processed' } except Exception as e: print(f"โŒ VERIFY: Error processing payment: {e}") return {'success': False, 'error': f'Processing error: {e}'} else: return {'success': False, 'error': 'No user ID in session'} elif session.payment_status == 'unpaid': return {'success': False, 'error': 'Payment not completed yet'} else: return {'success': False, 'error': f'Payment status: {session.payment_status}'} except stripe.error.StripeError as e: print(f"โŒ VERIFY: Stripe error: {e}") return {'success': False, 'error': str(e)} def handle_webhook(self, payload, signature): """Handle Stripe webhook events""" print(f"๐Ÿ” Processing webhook with signature: {bool(signature)}") try: event = stripe.Webhook.construct_event( payload, signature, settings.STRIPE_WEBHOOK_SECRET ) print(f"๐Ÿ“‹ Event type: {event['type']}") except ValueError as e: print(f"โŒ Invalid payload: {e}") return {'success': False, 'error': 'Invalid payload'} except stripe.error.SignatureVerificationError as e: print(f"โŒ Invalid signature: {e}") return {'success': False, 'error': 'Invalid signature'} if event['type'] == 'checkout.session.completed': session = event['data']['object'] print(f"๐Ÿ’ณ Processing checkout session: {session['id']}") # Process successful payment user_id = session.get('client_reference_id') amount = session['amount_total'] / 100 # Convert from cents print(f"๐Ÿ‘ค User ID: {user_id}, Amount: {amount} AED") print(f"๐Ÿ“‹ Session data: client_reference_id={session.get('client_reference_id')}") print(f"๐Ÿ“‹ Session metadata: {session.get('metadata', {})}") if user_id: try: user = User.objects.get(id=user_id) print(f"โœ… Found user: {user.email}, Current balance: {user.wallet_balance}") user.add_balance( amount=amount, description=f"Wallet top-up via Stripe", stripe_session_id=session['id'] ) user.refresh_from_db() print(f"๐Ÿ’ฐ New balance: {user.wallet_balance}") return {'success': True, 'message': 'Payment processed successfully'} except User.DoesNotExist: print(f"โŒ User not found: {user_id}") return {'success': False, 'error': 'User not found'} else: print("โŒ No user_id in session") return {'success': False, 'error': 'No user reference'} else: print(f"โ„น๏ธ Ignored event type: {event['type']}") return {'success': True, 'message': 'Event processed'} def process_refund(self, session_id, amount=None): """Process refund for a payment""" try: session = stripe.checkout.Session.retrieve(session_id) payment_intent = session.payment_intent if amount: refund = stripe.Refund.create( payment_intent=payment_intent, amount=int(amount * 100) # Convert to cents ) else: refund = stripe.Refund.create(payment_intent=payment_intent) return { 'success': True, 'refund_id': refund.id, 'amount': refund.amount / 100, 'status': refund.status } except stripe.error.StripeError as e: return {'success': False, 'error': str(e)}