mirror of https://github.com/kortix-ai/suna.git
critical: fix billing overcharge
This commit is contained in:
parent
c9890866fa
commit
677b02bbd8
|
@ -30,6 +30,7 @@ class CreateCheckoutSessionRequest(BaseModel):
|
|||
price_id: str
|
||||
success_url: str
|
||||
cancel_url: str
|
||||
commitment_type: Optional[str] = None
|
||||
|
||||
class CreatePortalSessionRequest(BaseModel):
|
||||
return_url: str
|
||||
|
@ -441,8 +442,9 @@ async def create_checkout_session(
|
|||
result = await subscription_service.create_checkout_session(
|
||||
account_id=account_id,
|
||||
price_id=request.price_id,
|
||||
success_url=request.success_url,
|
||||
cancel_url=request.cancel_url
|
||||
success_url=request.success_url,
|
||||
cancel_url=request.cancel_url,
|
||||
commitment_type=request.commitment_type
|
||||
)
|
||||
return result
|
||||
|
||||
|
|
|
@ -201,7 +201,7 @@ class SubscriptionService:
|
|||
'trial_ends_at': None
|
||||
}
|
||||
|
||||
async def create_checkout_session(self, account_id: str, price_id: str, success_url: str, cancel_url: str) -> Dict:
|
||||
async def create_checkout_session(self, account_id: str, price_id: str, success_url: str, cancel_url: str, commitment_type: Optional[str] = None) -> Dict:
|
||||
customer_id = await self.get_or_create_stripe_customer(account_id)
|
||||
|
||||
db = DBConnection()
|
||||
|
@ -247,7 +247,8 @@ class SubscriptionService:
|
|||
'account_id': account_id,
|
||||
'account_type': 'personal',
|
||||
'converting_from_trial': 'true',
|
||||
'previous_tier': current_tier or 'trial'
|
||||
'previous_tier': current_tier or 'trial',
|
||||
'commitment_type': commitment_type or 'none'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -314,7 +315,8 @@ class SubscriptionService:
|
|||
subscription_data={
|
||||
'metadata': {
|
||||
'account_id': account_id,
|
||||
'account_type': 'personal'
|
||||
'account_type': 'personal',
|
||||
'commitment_type': commitment_type or 'none'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -719,7 +721,6 @@ class SubscriptionService:
|
|||
return []
|
||||
|
||||
async def _track_commitment_if_needed(self, account_id: str, price_id: str, subscription: Dict, client):
|
||||
"""Track commitment if the subscription uses a commitment price ID"""
|
||||
if not is_commitment_price_id(price_id):
|
||||
return
|
||||
|
||||
|
@ -727,10 +728,14 @@ class SubscriptionService:
|
|||
if commitment_duration == 0:
|
||||
return
|
||||
|
||||
start_date = datetime.fromtimestamp(subscription['current_period_start'], tz=timezone.utc)
|
||||
end_date = start_date + timedelta(days=commitment_duration * 30) # Approximate months
|
||||
existing_commitment = await client.from_('commitment_history').select('id').eq('stripe_subscription_id', subscription['id']).execute()
|
||||
if existing_commitment.data:
|
||||
logger.info(f"[COMMITMENT] Commitment already tracked for subscription {subscription['id']}, skipping")
|
||||
return
|
||||
|
||||
start_date = datetime.fromtimestamp(subscription['current_period_start'], tz=timezone.utc)
|
||||
end_date = start_date + timedelta(days=commitment_duration * 30)
|
||||
|
||||
# Update credit_accounts with commitment info
|
||||
await client.from_('credit_accounts').update({
|
||||
'commitment_type': 'yearly_commitment',
|
||||
'commitment_start_date': start_date.isoformat(),
|
||||
|
@ -739,7 +744,7 @@ class SubscriptionService:
|
|||
'can_cancel_after': end_date.isoformat()
|
||||
}).eq('account_id', account_id).execute()
|
||||
|
||||
# Log in commitment history
|
||||
# Insert commitment history record
|
||||
await client.from_('commitment_history').insert({
|
||||
'account_id': account_id,
|
||||
'commitment_type': 'yearly_commitment',
|
||||
|
@ -749,7 +754,7 @@ class SubscriptionService:
|
|||
'stripe_subscription_id': subscription['id']
|
||||
}).execute()
|
||||
|
||||
logger.info(f"[COMMITMENT] Tracked yearly commitment for account {account_id}, ends {end_date.date()}")
|
||||
logger.info(f"[COMMITMENT] Tracked yearly commitment for account {account_id}, subscription {subscription['id']}, ends {end_date.date()}")
|
||||
|
||||
async def get_commitment_status(self, account_id: str) -> Dict:
|
||||
"""Get the commitment status for an account"""
|
||||
|
|
|
@ -253,6 +253,17 @@ class WebhookService:
|
|||
except Exception as e:
|
||||
logger.error(f"[WEBHOOK] Failed to update subscription metadata: {e}")
|
||||
|
||||
if event.type == 'customer.subscription.created':
|
||||
account_id = subscription.get('metadata', {}).get('account_id')
|
||||
price_id = subscription['items']['data'][0]['price']['id'] if subscription.get('items') else None
|
||||
commitment_type = subscription.get('metadata', {}).get('commitment_type')
|
||||
|
||||
if account_id and price_id and (
|
||||
is_commitment_price_id(price_id) or
|
||||
commitment_type == 'yearly_commitment'
|
||||
):
|
||||
await self._track_commitment(account_id, price_id, subscription, client)
|
||||
|
||||
from .subscription_service import subscription_service
|
||||
await subscription_service.handle_subscription_change(subscription)
|
||||
|
||||
|
@ -267,11 +278,14 @@ class WebhookService:
|
|||
logger.info(f"[WEBHOOK] Previous attributes: {previous_attributes}")
|
||||
logger.info(f"[WEBHOOK] Account ID from metadata: {subscription.metadata.get('account_id')}")
|
||||
|
||||
# Track commitment if price changed to a commitment plan
|
||||
price_id = subscription['items']['data'][0]['price']['id'] if subscription.get('items') else None
|
||||
prev_price_id = previous_attributes.get('items', {}).get('data', [{}])[0].get('price', {}).get('id') if previous_attributes.get('items') else None
|
||||
commitment_type = subscription.metadata.get('commitment_type')
|
||||
|
||||
if price_id and price_id != prev_price_id and is_commitment_price_id(price_id):
|
||||
if price_id and (
|
||||
(price_id != prev_price_id and is_commitment_price_id(price_id)) or
|
||||
(commitment_type == 'yearly_commitment' and is_commitment_price_id(price_id))
|
||||
):
|
||||
account_id = subscription.metadata.get('account_id')
|
||||
if account_id:
|
||||
await self._track_commitment(account_id, price_id, subscription, client)
|
||||
|
@ -756,6 +770,11 @@ class WebhookService:
|
|||
if commitment_duration == 0:
|
||||
return
|
||||
|
||||
existing_commitment = await client.from_('commitment_history').select('id').eq('stripe_subscription_id', subscription['id']).execute()
|
||||
if existing_commitment.data:
|
||||
logger.info(f"[WEBHOOK COMMITMENT] Commitment already tracked for subscription {subscription['id']}, skipping")
|
||||
return
|
||||
|
||||
start_date = datetime.fromtimestamp(subscription['current_period_start'], tz=timezone.utc)
|
||||
end_date = start_date + timedelta(days=365)
|
||||
|
||||
|
@ -767,16 +786,16 @@ class WebhookService:
|
|||
'can_cancel_after': end_date.isoformat()
|
||||
}).eq('account_id', account_id).execute()
|
||||
|
||||
await client.from_('commitment_history').upsert({
|
||||
await client.from_('commitment_history').insert({
|
||||
'account_id': account_id,
|
||||
'commitment_type': 'yearly_commitment',
|
||||
'price_id': price_id,
|
||||
'start_date': start_date.isoformat(),
|
||||
'end_date': end_date.isoformat(),
|
||||
'stripe_subscription_id': subscription['id']
|
||||
}, on_conflict='account_id,stripe_subscription_id').execute()
|
||||
}).execute()
|
||||
|
||||
logger.info(f"[WEBHOOK COMMITMENT] Tracked yearly commitment for account {account_id}, ends {end_date.date()}")
|
||||
logger.info(f"[WEBHOOK COMMITMENT] Tracked yearly commitment for account {account_id}, subscription {subscription['id']}, ends {end_date.date()}")
|
||||
|
||||
|
||||
webhook_service = WebhookService()
|
Loading…
Reference in New Issue