66import uuid
77
88import stripe
9- from fastapi import APIRouter , Depends , HTTPException , Query , Request , Response
9+ from fastapi import APIRouter , Depends , Query , Request , Response
1010from fastapi .responses import RedirectResponse
1111from fastapi .templating import Jinja2Templates
1212from sqlmodel import Session
1313
1414from exceptions .http_exceptions import (
15+ ActiveSubscriptionError ,
1516 InsufficientPermissionsError ,
1617 OrganizationNotFoundError ,
18+ StripeServiceUnavailableError ,
19+ StripeSessionError ,
1720)
1821from utils .app .billing import (
1922 billing_status ,
@@ -54,15 +57,6 @@ def _require_org_member(user: User, org_id: int):
5457 return org
5558
5659
57- def _user_permissions_for_org (user : User , org_id : int ) -> set :
58- permissions : set = set ()
59- for role in user .roles :
60- if role .organization_id == org_id :
61- for permission in role .permissions :
62- permissions .add (permission .name )
63- return permissions
64-
65-
6660@router .get ("/{org_id}/billing" )
6761async def read_billing (
6862 org_id : int ,
@@ -71,14 +65,13 @@ async def read_billing(
7165 session : Session = Depends (get_session ),
7266):
7367 org = _require_org_member (user , org_id )
74- permissions = _user_permissions_for_org (user , org_id )
75- if AppPermissions .VIEW_BILLING not in permissions :
68+ if not user .has_permission (AppPermissions .VIEW_BILLING , org_id ):
7669 raise InsufficientPermissionsError ()
7770
7871 billing = get_org_billing (session , org_id )
7972 status = billing_status (billing )
8073 settings = stripe_settings ()
81- can_manage = AppPermissions .MANAGE_BILLING in permissions
74+ can_manage = user . has_permission ( AppPermissions .MANAGE_BILLING , org_id )
8275
8376 return templates .TemplateResponse (
8477 request ,
@@ -140,9 +133,7 @@ async def start_checkout(
140133 "Checkout blocked for org_id=%s: organization already has an active subscription" ,
141134 org_id ,
142135 )
143- raise InsufficientPermissionsError (
144- "This organization already has an active subscription."
145- )
136+ raise ActiveSubscriptionError ()
146137
147138 assert user .account is not None
148139 billing = get_org_billing (session , org_id )
@@ -158,14 +149,11 @@ async def start_checkout(
158149 logger .exception (
159150 "Stripe Checkout session creation failed for org_id=%s" , org_id
160151 )
161- raise HTTPException (
162- status_code = 503 ,
163- detail = "Unable to start checkout. Please try again later." ,
164- ) from None
152+ raise StripeServiceUnavailableError ("start checkout" ) from None
165153
166154 checkout_url = checkout .url
167155 if not checkout_url :
168- raise InsufficientPermissionsError ( "Stripe did not return a checkout URL. " )
156+ raise StripeSessionError ( " checkout" )
169157 return _redirect_to_stripe (request , checkout_url )
170158
171159
@@ -193,14 +181,11 @@ async def start_customer_portal(
193181 )
194182 except stripe .StripeError :
195183 logger .exception ("Stripe Customer Portal session failed for org_id=%s" , org_id )
196- raise HTTPException (
197- status_code = 503 ,
198- detail = "Unable to open billing portal. Please try again later." ,
199- ) from None
184+ raise StripeServiceUnavailableError ("open billing portal" ) from None
200185
201186 portal_url = portal .url
202187 if not portal_url :
203- raise InsufficientPermissionsError ( "Stripe did not return a portal URL. " )
188+ raise StripeSessionError ( " portal" )
204189 return _redirect_to_stripe (request , portal_url )
205190
206191
@@ -313,7 +298,7 @@ async def stripe_webhook(
313298 return Response (status_code = 200 , content = '{"ok": true}' )
314299
315300 result = handle_stripe_webhook_event (session , event )
316- if result is WebhookHandleResult .FAILED :
301+ if result in { WebhookHandleResult .FAILED , WebhookHandleResult . RETRYABLE } :
317302 release_webhook_event_claim (session , event .id )
318303 return Response (status_code = 500 , content = "Webhook handler failed" )
319304
0 commit comments