Add Stripe Customer Portal session API for account billing (#35)
## Summary - Companion to [chat_web_app#33](ai_ml_operations/chat_web_app#33) (Account billing + Customer Portal) - Follow-on from finance MVP [#21](#21): add authenticated `POST /api/finance/portal/` that creates a Stripe Billing Portal session and returns `portal_url` - Resolve Stripe customer from the user's latest `Invoice.stripe_customer_id`; return `400` when missing (user must complete Checkout first) - Document `STRIPE_PORTAL_RETURN_URL` (default `{FRONTEND_BASE_URL}/account/`) in settings + env examples ## Test plan - [ ] `manage.py test finance.tests.test_portal finance.tests.test_checkout` - [ ] Authenticated portal create with invoice that has `stripe_customer_id` → `201` + `portal_url` - [ ] No customer / unpaid user → `400` with clear detail - [ ] Missing `STRIPE_SECRET_KEY` → `503` - [ ] Unauthenticated → `401` - [ ] Custom `return_url` in body overrides default portal return URLReviewed-on: #35
This commit was merged in pull request #35.
This commit is contained in:
@@ -11,10 +11,13 @@ from finance.serializers import (
|
||||
CheckoutSessionSerializer,
|
||||
InvoiceSerializer,
|
||||
PaymentSerializer,
|
||||
PortalSessionSerializer,
|
||||
)
|
||||
from finance.services.stripe_service import (
|
||||
StripeNotConfiguredError,
|
||||
create_billing_portal_session,
|
||||
create_checkout_session,
|
||||
resolve_stripe_customer_id,
|
||||
)
|
||||
from finance.services.webhooks import dispatch_stripe_event
|
||||
|
||||
@@ -72,6 +75,48 @@ class CreateCheckoutSessionView(APIView):
|
||||
)
|
||||
|
||||
|
||||
class CreateBillingPortalSessionView(APIView):
|
||||
"""Create a Stripe Customer Portal session and return the hosted URL."""
|
||||
|
||||
def post(self, request):
|
||||
serializer = PortalSessionSerializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
customer_id = resolve_stripe_customer_id(user=request.user)
|
||||
if not customer_id:
|
||||
return Response(
|
||||
{
|
||||
"detail": (
|
||||
"No Stripe customer found for this account. "
|
||||
"Complete Checkout first to manage billing."
|
||||
)
|
||||
},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
try:
|
||||
session = create_billing_portal_session(
|
||||
customer_id=customer_id,
|
||||
return_url=serializer.validated_data.get("return_url"),
|
||||
)
|
||||
except StripeNotConfiguredError as exc:
|
||||
return Response(
|
||||
{"detail": str(exc)},
|
||||
status=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||
)
|
||||
except stripe.StripeError as exc:
|
||||
logger.exception("Stripe Billing Portal Session creation failed")
|
||||
return Response(
|
||||
{"detail": str(getattr(exc, "user_message", None) or exc)},
|
||||
status=status.HTTP_502_BAD_GATEWAY,
|
||||
)
|
||||
|
||||
return Response(
|
||||
{"portal_url": session.url},
|
||||
status=status.HTTP_201_CREATED,
|
||||
)
|
||||
|
||||
|
||||
class InvoiceListView(APIView):
|
||||
def get(self, request):
|
||||
invoices = Invoice.objects.filter(user=request.user)
|
||||
|
||||
Reference in New Issue
Block a user