billing realted PK bank transfer settigns

This commit is contained in:
IGNY8 VPS (Salman)
2026-01-20 03:35:35 +00:00
parent 4996e2e1aa
commit 0957ece3a4
4 changed files with 302 additions and 72 deletions

View File

@@ -879,6 +879,129 @@ class CreditPackageViewSet(viewsets.ReadOnlyModelViewSet):
{'count': paginator.page.paginator.count, 'next': paginator.get_next_link(), 'previous': paginator.get_previous_link(), 'results': results},
request=request
)
@action(detail=True, methods=['post'])
def purchase(self, request, pk=None):
"""
Purchase a credit package - creates an invoice for manual payment.
For Stripe/PayPal, use the dedicated checkout endpoints.
This endpoint is specifically for bank transfer/manual payments.
Request body:
{
"payment_method": "bank_transfer" // Required for manual/bank transfer
}
Returns:
{
"invoice_id": 123,
"invoice_number": "INV-2025-001",
"total_amount": "99.00",
"currency": "USD",
"status": "pending",
"message": "Invoice created. Please submit payment confirmation."
}
"""
payment_method = request.data.get('payment_method', 'bank_transfer')
# Only allow manual/bank_transfer through this endpoint
if payment_method not in ['bank_transfer', 'manual', 'local_wallet']:
return error_response(
error='Use dedicated Stripe/PayPal endpoints for card payments',
status_code=status.HTTP_400_BAD_REQUEST,
request=request
)
try:
package = self.get_queryset().get(pk=pk)
except CreditPackage.DoesNotExist:
return error_response(
error='Credit package not found',
status_code=status.HTTP_404_NOT_FOUND,
request=request
)
# Get the account
account = getattr(request, 'account', None)
if not account:
return error_response(
error='Account not found',
status_code=status.HTTP_400_BAD_REQUEST,
request=request
)
# Check if account has an active status (allow purchases only for active accounts)
if account.status not in ['active', 'trial']:
return error_response(
error='Account must be active to purchase credits. Please complete your subscription first.',
status_code=status.HTTP_400_BAD_REQUEST,
request=request
)
# Check for existing pending invoice for this package
existing_pending = Invoice.objects.filter(
account=account,
status='pending',
metadata__credit_package_id=package.id
).first()
if existing_pending:
return success_response(
data={
'invoice_id': existing_pending.id,
'invoice_number': existing_pending.invoice_number,
'total_amount': str(existing_pending.total),
'currency': existing_pending.currency,
'status': existing_pending.status,
'message': 'You already have a pending invoice for this credit package.',
'next_action': 'submit_payment'
},
request=request
)
try:
with transaction.atomic():
# Create invoice for the credit package
invoice = InvoiceService.create_credit_package_invoice(
account=account,
credit_package=package
)
# Update invoice payment method
invoice.payment_method = payment_method
invoice.save()
logger.info(
f"Credit package invoice created: {invoice.invoice_number} "
f"for account {account.id}, package {package.name} ({package.credits} credits)"
)
return success_response(
data={
'invoice_id': invoice.id,
'invoice_number': invoice.invoice_number,
'total_amount': str(invoice.total),
'currency': invoice.currency,
'status': invoice.status,
'credit_package': {
'id': package.id,
'name': package.name,
'credits': package.credits,
},
'message': 'Invoice created successfully. Please submit your bank transfer payment confirmation.',
'next_action': 'submit_payment'
},
request=request,
status_code=status.HTTP_201_CREATED
)
except Exception as e:
logger.error(f"Failed to create credit package invoice: {str(e)}", exc_info=True)
return error_response(
error=f'Failed to create invoice: {str(e)}',
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
request=request
)
class AccountPaymentMethodViewSet(AccountModelViewSet):

View File

@@ -599,7 +599,7 @@ class CreditPackage(models.Model):
# Display
description = models.TextField(blank=True)
features = models.JSONField(default=list, help_text="Bonus features or highlights")
features = models.JSONField(default=list, blank=True, help_text="Bonus features or highlights")
# Sort order
sort_order = models.IntegerField(default=0, help_text="Display order (lower = first)")

View File

@@ -182,16 +182,28 @@ class InvoiceService:
local_currency = get_currency_for_country(account.billing_country) if account.billing_country else 'USD'
local_equivalent = convert_usd_to_local(usd_price, account.billing_country) if local_currency != 'USD' else usd_price
# Get billing email from account
billing_email = account.billing_email
if not billing_email:
owner = account.users.filter(role='owner').first()
if owner:
billing_email = owner.email
invoice = Invoice.objects.create(
account=account,
invoice_number=InvoiceService.generate_invoice_number(account),
billing_email=account.billing_email or account.users.filter(role='owner').first().email,
status='pending',
currency=currency,
invoice_date=invoice_date,
due_date=invoice_date + timedelta(days=INVOICE_DUE_DATE_OFFSET),
metadata={
'billing_snapshot': {
'email': billing_email,
'name': account.name,
'billing_address': account.billing_address or '',
},
'credit_package_id': credit_package.id,
'credit_package_name': credit_package.name,
'credit_amount': credit_package.credits,
'usd_price': str(credit_package.price), # Store original USD price
'local_currency': local_currency, # Store local currency code for display
@@ -233,15 +245,27 @@ class InvoiceService:
notes: Invoice notes
due_date: Payment due date
"""
# Get billing email
email = billing_email or account.billing_email
if not email:
owner = account.users.filter(role='owner').first()
if owner:
email = owner.email
invoice = Invoice.objects.create(
account=account,
invoice_number=InvoiceService.generate_invoice_number(account),
billing_email=billing_email or account.billing_email or account.users.filter(role='owner').first().email,
status='draft',
currency='USD',
notes=notes,
invoice_date=timezone.now().date(),
due_date=due_date or (timezone.now() + timedelta(days=30))
due_date=due_date or (timezone.now() + timedelta(days=30)),
metadata={
'billing_snapshot': {
'email': email,
'name': account.name,
}
}
)
# Add all line items