Skip to content

Commit 77569fc

Browse files
authored
Merge pull request #11 from API-200/feature/payments
Feature/payments
2 parents a6ffdf9 + 4979974 commit 77569fc

File tree

19 files changed

+706
-12
lines changed

19 files changed

+706
-12
lines changed

packages/backend/src/modules/base/checkUsage.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
1+
import { PLANS } from '@utils/constants';
12
import { supabase } from '../../utils/supabase';
2-
import { BASIC_PLAN_MAX_REQUESTS } from '../../utils/constants';
33

44
export async function checkUsage(
55
userId: string,
66
): Promise<{ error?: string; status?: number }> {
7+
8+
const { data: proSubscriptionExists, error: subscriptionCheckError } = await supabase.rpc('check_subscription', { p_user_id: userId });
9+
const maxRequests = proSubscriptionExists ? PLANS.PRO.REQUESTS_PER_MONTH : PLANS.BASIC.REQUESTS_PER_MONTH;
10+
11+
if (subscriptionCheckError) {
12+
console.error('Error checking subscription:', subscriptionCheckError);
13+
return { error: 'API200 Error: Internal server error', status: 500 };
14+
}
15+
716
const { data, error } = await supabase.rpc('increment_usage', {
817
p_user_id: userId,
9-
p_max_requests: BASIC_PLAN_MAX_REQUESTS,
18+
p_max_requests: maxRequests,
1019
});
1120

1221
if (error) {
@@ -19,7 +28,7 @@ export async function checkUsage(
1928

2029
if (!result?.allowed) {
2130
return {
22-
error: `API200 Error: Monthly usage limit exceeded (${result?.c_count}/${BASIC_PLAN_MAX_REQUESTS})`,
31+
error: `API200 Error: Monthly usage limit exceeded (${result?.c_count}/${maxRequests})`,
2332
status: 429,
2433
};
2534
}
Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,10 @@
1-
export const BASIC_PLAN_MAX_REQUESTS = 1000;
21
export const DAY = 24 * 60 * 60 * 1000;
2+
3+
export const PLANS = {
4+
BASIC: {
5+
REQUESTS_PER_MONTH: 100
6+
},
7+
PRO: {
8+
REQUESTS_PER_MONTH: 10000
9+
}
10+
} as const;

packages/backend/src/utils/database.types.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,27 @@ export type Database = {
3030
}
3131
Relationships: []
3232
}
33+
customers: {
34+
Row: {
35+
created_at: string
36+
customer_id: string
37+
email: string
38+
updated_at: string
39+
}
40+
Insert: {
41+
created_at?: string
42+
customer_id: string
43+
email: string
44+
updated_at?: string
45+
}
46+
Update: {
47+
created_at?: string
48+
customer_id?: string
49+
email?: string
50+
updated_at?: string
51+
}
52+
Relationships: []
53+
}
3354
endpoints: {
3455
Row: {
3556
cache_enabled: boolean
@@ -314,23 +335,73 @@ export type Database = {
314335
}
315336
Relationships: []
316337
}
338+
subscriptions: {
339+
Row: {
340+
billing_cycle: string
341+
created_at: string
342+
customer_id: string
343+
next_billed_at: string
344+
price_id: string | null
345+
product_id: string | null
346+
scheduled_change: string | null
347+
subscription_id: string
348+
subscription_status: string
349+
updated_at: string
350+
}
351+
Insert: {
352+
billing_cycle: string
353+
created_at?: string
354+
customer_id: string
355+
next_billed_at: string
356+
price_id?: string | null
357+
product_id?: string | null
358+
scheduled_change?: string | null
359+
subscription_id: string
360+
subscription_status: string
361+
updated_at?: string
362+
}
363+
Update: {
364+
billing_cycle?: string
365+
created_at?: string
366+
customer_id?: string
367+
next_billed_at?: string
368+
price_id?: string | null
369+
product_id?: string | null
370+
scheduled_change?: string | null
371+
subscription_id?: string
372+
subscription_status?: string
373+
updated_at?: string
374+
}
375+
Relationships: [
376+
{
377+
foreignKeyName: "subscriptions_customer_id_fkey"
378+
columns: ["customer_id"]
379+
isOneToOne: false
380+
referencedRelation: "customers"
381+
referencedColumns: ["customer_id"]
382+
},
383+
]
384+
}
317385
usages: {
318386
Row: {
319387
billing_started_at: string
320388
calls_count: number
321389
id: number
390+
updated_at: string | null
322391
user_id: string
323392
}
324393
Insert: {
325394
billing_started_at?: string
326395
calls_count?: number
327396
id?: number
397+
updated_at?: string | null
328398
user_id: string
329399
}
330400
Update: {
331401
billing_started_at?: string
332402
calls_count?: number
333403
id?: number
404+
updated_at?: string | null
334405
user_id?: string
335406
}
336407
Relationships: []
@@ -340,6 +411,10 @@ export type Database = {
340411
[_ in never]: never
341412
}
342413
Functions: {
414+
check_subscription: {
415+
Args: { p_user_id: string }
416+
Returns: boolean
417+
}
343418
get_route_data: {
344419
Args: {
345420
p_service_name: string

packages/dashboard/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
"@hookform/resolvers": "^3.10.0",
1515
"@keyv/redis": "^4.3.2",
1616
"@monaco-editor/react": "^4.7.0",
17+
"@paddle/paddle-js": "^1.4.1",
18+
"@paddle/paddle-node-sdk": "^2.7.1",
1719
"@radix-ui/react-accordion": "^1.2.3",
1820
"@radix-ui/react-alert-dialog": "^1.1.6",
1921
"@radix-ui/react-avatar": "^1.1.3",

packages/dashboard/pnpm-lock.yaml

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/dashboard/src/app/(layout)/components/AppSidebar.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use client"
22
import Link from "next/link"
33
import { usePathname } from "next/navigation"
4-
import { AlertCircleIcon, Book, Globe, KeyRound, Layers } from 'lucide-react'
4+
import {AlertCircleIcon, Book, CreditCard, Globe, KeyRound, Layers} from 'lucide-react'
55
import Image from "next/image"
66
import {
77
Sidebar,
@@ -102,6 +102,15 @@ export function AppSidebar() {
102102
</Link>
103103
</SidebarMenuButton>
104104
</SidebarMenuItem>
105+
{FEATURES.SIDEBAR.SHOW_USAGE && <SidebarMenuItem>
106+
<SidebarMenuButton asChild isActive={isActive("/subscription")}>
107+
<Link href="/subscription">
108+
<CreditCard className="mr-2 h-4 w-4" />
109+
Subscription
110+
</Link>
111+
</SidebarMenuButton>
112+
</SidebarMenuItem>}
113+
105114
<GiveFeedbackLink />
106115
</SidebarMenu>
107116
</SidebarGroupContent>

packages/dashboard/src/app/(layout)/components/UsageProgressBar.tsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,41 @@
11
import { Progress } from "@/components/ui/progress"
22
import { useEffect, useState } from "react";
33
import { createClient } from "@/utils/supabase/client";
4-
5-
const MAX_REQUESTS_PER_MONTH = 1000
4+
import { PLANS } from '@/utils/constants';
5+
import { Badge } from '@/components/ui/badge';
66

77
export function UsageProgressBar() {
88
const [usages, setUsages] = useState<number>(0)
9+
const [percentage, setPercentage] = useState<number>(0)
10+
const [maxRequestsPerMonth, setMaxRequestsPerMonth] = useState<number>(0)
11+
const [isPro, setIsPro] = useState<boolean>(false)
912
const supabase = createClient()
1013

11-
1214
useEffect(() => {
1315
const fetchData = async () => {
1416
const { data } = await supabase.from('usages').select().maybeSingle()
1517
setUsages(data?.calls_count ?? 0)
18+
19+
const user = await supabase.auth.getUser()
20+
const { data: proSubscriptionExists } = await supabase.rpc('check_subscription', { p_user_id: user.data.user?.id })
21+
22+
setIsPro(proSubscriptionExists)
23+
setMaxRequestsPerMonth(proSubscriptionExists ? PLANS.PRO.REQUESTS_PER_MONTH : PLANS.BASIC.REQUESTS_PER_MONTH)
24+
setPercentage(Math.min((usages / maxRequestsPerMonth) * 100, 100))
1625
}
1726
fetchData()
18-
}, [supabase]) // Added supabase to the dependency array
19-
const percentage = Math.min((usages / MAX_REQUESTS_PER_MONTH) * 100, 100)
27+
}, [])
2028

2129
return (
2230
<div className="px-4 space-y-2">
2331
<div className="flex justify-between text-sm">
2432
<span>Requests this month</span>
2533
<span>
26-
{usages} / {MAX_REQUESTS_PER_MONTH}
34+
{usages} / {maxRequestsPerMonth}
2735
</span>
2836
</div>
2937
<Progress value={percentage} className="w-full" />
38+
{isPro && <Badge>Pro</Badge>}
3039
</div>
3140
)
3241
}

0 commit comments

Comments
 (0)