billing realted PK bank transfer settigns
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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)")
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user