""" Billing Views - Payment confirmation and management """ from rest_framework import viewsets, status from rest_framework.decorators import action from django.db import transaction from django.utils import timezone from datetime import timedelta from igny8_core.api.response import success_response, error_response from igny8_core.api.permissions import IsAdminOrOwner from igny8_core.auth.models import Account, Subscription from igny8_core.business.billing.services.credit_service import CreditService from igny8_core.business.billing.models import CreditTransaction import logging logger = logging.getLogger(__name__) class BillingViewSet(viewsets.GenericViewSet): """ ViewSet for billing operations (admin-only). """ permission_classes = [IsAdminOrOwner] @action(detail=False, methods=['post'], url_path='confirm-bank-transfer') def confirm_bank_transfer(self, request): """ Confirm a bank transfer payment and activate/renew subscription. Request body: { "account_id": 123, "external_payment_id": "BT-2025-001", "amount": "29.99", "payer_name": "John Doe", "proof_url": "https://...", "period_months": 1 } """ account_id = request.data.get('account_id') subscription_id = request.data.get('subscription_id') external_payment_id = request.data.get('external_payment_id') amount = request.data.get('amount') payer_name = request.data.get('payer_name') proof_url = request.data.get('proof_url') period_months = int(request.data.get('period_months', 1)) if not all([external_payment_id, amount, payer_name]): return error_response( error='external_payment_id, amount, and payer_name are required', status_code=status.HTTP_400_BAD_REQUEST, request=request ) if not account_id and not subscription_id: return error_response( error='Either account_id or subscription_id is required', status_code=status.HTTP_400_BAD_REQUEST, request=request ) try: with transaction.atomic(): # Get account if account_id: account = Account.objects.select_related('plan').get(id=account_id) subscription = getattr(account, 'subscription', None) else: subscription = Subscription.objects.select_related('account', 'account__plan').get(id=subscription_id) account = subscription.account if not account or not account.plan: return error_response( error='Account or plan not found', status_code=status.HTTP_404_NOT_FOUND, request=request ) # Calculate period dates based on billing cycle now = timezone.now() if account.plan.billing_cycle == 'monthly': period_end = now + timedelta(days=30 * period_months) elif account.plan.billing_cycle == 'annual': period_end = now + timedelta(days=365 * period_months) else: period_end = now + timedelta(days=30 * period_months) # Create or update subscription if not subscription: subscription = Subscription.objects.create( account=account, payment_method='bank_transfer', external_payment_id=external_payment_id, status='active', current_period_start=now, current_period_end=period_end, cancel_at_period_end=False ) else: subscription.payment_method = 'bank_transfer' subscription.external_payment_id = external_payment_id subscription.status = 'active' subscription.current_period_start = now subscription.current_period_end = period_end subscription.cancel_at_period_end = False subscription.save() # Update account account.payment_method = 'bank_transfer' account.status = 'active' monthly_credits = account.plan.get_effective_credits_per_month() account.credits = monthly_credits account.save() # Log transaction CreditTransaction.objects.create( account=account, transaction_type='subscription', amount=monthly_credits, balance_after=monthly_credits, description=f'Bank transfer payment confirmed: {external_payment_id}', metadata={ 'external_payment_id': external_payment_id, 'amount': str(amount), 'payer_name': payer_name, 'proof_url': proof_url if proof_url else '', 'period_months': period_months, 'confirmed_by': request.user.email } ) logger.info( f'Bank transfer confirmed for account {account.id}: ' f'{external_payment_id}, {amount}, {monthly_credits} credits added' ) return success_response( data={ 'account_id': account.id, 'subscription_id': subscription.id, 'status': 'active', 'credits': account.credits, 'period_start': subscription.current_period_start.isoformat(), 'period_end': subscription.current_period_end.isoformat() }, message='Bank transfer confirmed successfully', request=request ) except Account.DoesNotExist: return error_response( error='Account not found', status_code=status.HTTP_404_NOT_FOUND, request=request ) except Subscription.DoesNotExist: return error_response( error='Subscription not found', status_code=status.HTTP_404_NOT_FOUND, request=request ) except Exception as e: logger.error(f'Error confirming bank transfer: {str(e)}', exc_info=True) return error_response( error=f'Failed to confirm payment: {str(e)}', status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, request=request )