billing stuff

This commit is contained in:
Adam Cohen Hillel 2025-04-21 14:26:09 +01:00
parent 4bcbea3464
commit 24d95d278c
4 changed files with 34 additions and 15 deletions

View File

@ -1,12 +1,11 @@
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import Dict, Optional, Tuple from typing import Dict, Optional, Tuple
from services.supabase import DBConnection
# Define subscription tiers and their monthly hour limits # Define subscription tiers and their monthly limits (in minutes)
SUBSCRIPTION_TIERS = { SUBSCRIPTION_TIERS = {
'price_1RDQbOG6l1KZGqIrgrYzMbnL': {'name': 'free', 'hours': 100}, 'price_1RGJ9GG6l1KZGqIroxSqgphC': {'name': 'free', 'minutes': 10},
'price_1RC2PYG6l1KZGqIrpbzFB9Lp': {'name': 'base', 'hours': 100}, 'price_1RGJ9LG6l1KZGqIrd9pwzeNW': {'name': 'base', 'minutes': 6000}, # 100 hours = 6000 minutes
'price_1RDQWqG6l1KZGqIrChli4Ys4': {'name': 'extra', 'hours': 100} 'price_1RGJ9JG6l1KZGqIrVUU4ZRv6': {'name': 'extra', 'minutes': 6000} # 100 hours = 6000 minutes
} }
async def get_account_subscription(client, account_id: str) -> Optional[Dict]: async def get_account_subscription(client, account_id: str) -> Optional[Dict]:
@ -24,7 +23,7 @@ async def get_account_subscription(client, account_id: str) -> Optional[Dict]:
return None return None
async def calculate_monthly_usage(client, account_id: str) -> float: async def calculate_monthly_usage(client, account_id: str) -> float:
"""Calculate total agent run hours for the current month for an account.""" """Calculate total agent run minutes for the current month for an account."""
# Get start of current month in UTC # Get start of current month in UTC
now = datetime.now(timezone.utc) now = datetime.now(timezone.utc)
start_of_month = datetime(now.year, now.month, 1, tzinfo=timezone.utc) start_of_month = datetime(now.year, now.month, 1, tzinfo=timezone.utc)
@ -50,7 +49,7 @@ async def calculate_monthly_usage(client, account_id: str) -> float:
if not runs_result.data: if not runs_result.data:
return 0.0 return 0.0
# Calculate total hours # Calculate total minutes
total_seconds = 0 total_seconds = 0
now_ts = now.timestamp() now_ts = now.timestamp()
@ -64,7 +63,7 @@ async def calculate_monthly_usage(client, account_id: str) -> float:
total_seconds += (end_time - start_time) total_seconds += (end_time - start_time)
return total_seconds / 3600 # Convert to hours return total_seconds / 60 # Convert to minutes
async def check_billing_status(client, account_id: str) -> Tuple[bool, str, Optional[Dict]]: async def check_billing_status(client, account_id: str) -> Tuple[bool, str, Optional[Dict]]:
""" """
@ -79,7 +78,7 @@ async def check_billing_status(client, account_id: str) -> Tuple[bool, str, Opti
# If no subscription, they can use free tier # If no subscription, they can use free tier
if not subscription: if not subscription:
subscription = { subscription = {
'price_id': 'price_1RDQbOG6l1KZGqIrgrYzMbnL', # Free tier 'price_id': 'price_1RGJ9GG6l1KZGqIroxSqgphC', # Free tier
'plan_name': 'Free' 'plan_name': 'Free'
} }
@ -92,8 +91,8 @@ async def check_billing_status(client, account_id: str) -> Tuple[bool, str, Opti
current_usage = await calculate_monthly_usage(client, account_id) current_usage = await calculate_monthly_usage(client, account_id)
# Check if within limits # Check if within limits
if current_usage >= tier_info['hours']: if current_usage >= tier_info['minutes']:
return False, f"Monthly limit of {tier_info['hours']} hours reached. Please upgrade your plan or wait until next month.", subscription return False, f"Monthly limit of {tier_info['minutes']} minutes reached. Please upgrade your plan or wait until next month.", subscription
return True, "OK", subscription return True, "OK", subscription

View File

@ -9,9 +9,9 @@ import { SubmitButton } from "@/components/ui/submit-button";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { siteConfig } from "@/lib/home"; import { siteConfig } from "@/lib/home";
export const SUBSCRIPTION_PLANS = { export const SUBSCRIPTION_PLANS = {
FREE: 'price_1RDQbOG6l1KZGqIrgrYzMbnL', FREE: 'price_1RGJ9GG6l1KZGqIroxSqgphC',
BASIC: 'price_1RC2PYG6l1KZGqIrpbzFB9Lp', BASIC: 'price_1RGJ9LG6l1KZGqIrd9pwzeNW',
PRO: 'price_1RDQWqG6l1KZGqIrChli4Ys4' PRO: 'price_1RGJ9JG6l1KZGqIrVUU4ZRv6'
} as const; } as const;
interface PlanComparisonProps { interface PlanComparisonProps {
@ -171,6 +171,16 @@ export function PlanComparison({
</li> </li>
))} ))}
</ul> </ul>
{(tier as any).showContactSales && (
<Button
variant="outline"
className="w-full h-10 rounded-full font-medium transition-colors mt-4"
onClick={() => window.open('mailto:support@kortix.ai?subject=Enterprise Plan Inquiry', '_blank')}
>
Need more? Contact Sales
</Button>
)}
</div> </div>
</div> </div>
); );

View File

@ -366,6 +366,15 @@ export function PricingSection() {
</li> </li>
))} ))}
</ul> </ul>
{(tier as any).showContactSales && (
<button
className="h-10 w-full flex items-center justify-center text-sm font-normal tracking-wide rounded-full px-4 cursor-pointer transition-all ease-out active:scale-95 bg-transparent border border-secondary/20 text-secondary shadow-[0px_1px_2px_0px_rgba(255,255,255,0.05)_inset] mt-6"
onClick={() => window.open('mailto:support@kortix.ai?subject=Enterprise Plan Inquiry', '_blank')}
>
Need more? Contact Sales
</button>
)}
</div> </div>
</div> </div>
))} ))}

View File

@ -111,7 +111,7 @@ export const siteConfig = {
name: "Enterprise", name: "Enterprise",
price: "$199", price: "$199",
description: "For organizations with complex needs", description: "For organizations with complex needs",
buttonText: "Contact Sales", buttonText: "Hire Suna",
buttonColor: "bg-secondary text-white", buttonColor: "bg-secondary text-white",
isPopular: false, isPopular: false,
hours: "40 hours", hours: "40 hours",
@ -123,6 +123,7 @@ export const siteConfig = {
"Service level agreement", "Service level agreement",
"Custom AI model training", "Custom AI model training",
], ],
showContactSales: true,
}, },
], ],
companyShowcase: { companyShowcase: {