Skip to content

Commit 9c9e634

Browse files
committed
[paywall] [checkout] Create checkout and paywall
1 parent d6bddf4 commit 9c9e634

4 files changed

Lines changed: 42 additions & 3 deletions

File tree

src/app/api/ai/route.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { headers } from 'next/headers';
22
import { auth } from '@/lib/auth';
3-
import { deductCredits } from '@/lib/user-entitlement';
3+
import { deductCredits, getUserEntitlement, hasCredits } from '@/lib/user-entitlement';
44
import { getAiResponse } from '@/lib/ai';
55

66
export async function POST(request: Request) {
@@ -18,6 +18,28 @@ export async function POST(request: Request) {
1818
);
1919
}
2020

21+
const entitlement = await getUserEntitlement(session.user.id);
22+
23+
if (!entitlement) {
24+
return Response.json(
25+
{
26+
code: 'no_active_purchase',
27+
message: 'You do not have an active license to use this feature.',
28+
},
29+
{ status: 403 }
30+
);
31+
}
32+
33+
if (!(await hasCredits(session.user.id, 100))) {
34+
return Response.json(
35+
{
36+
code: 'insufficient_credits',
37+
message: 'You do not have enough credits to use this feature.',
38+
},
39+
{ status: 403 }
40+
);
41+
}
42+
2143
/**
2244
* Here you would implement the AI asset generation and credit consumption logic.
2345
* For demonstration, we will just return a dummy response and deduct 100 credits.

src/app/chat/ai-app.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,28 @@
33
import { useState } from 'react';
44
import LoginModal from '@/components/login-modal';
55
import { AIChat } from '@/components/ai-chat';
6+
import { Paywall, usePaywall } from '@/react-starter/components/paywall';
67

78
export default function AiApp(props: { examples: string[] }) {
89
const { examples } = props;
910
const [isShowingLogin, setIsShowingLogin] = useState<boolean>(false);
11+
const { hidePaywall, state, showNoActivePurchase, showInsufficientCredits } = usePaywall();
1012

1113
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1214
const handleApiError = (data: any) => {
1315
if (data.code === 'unauthenticated') {
1416
setIsShowingLogin(true);
17+
} else if (data.code === 'no_active_purchase') {
18+
showNoActivePurchase();
19+
} else if (data.code === 'insufficient_credits') {
20+
showInsufficientCredits();
1521
}
1622
};
1723

1824
return (
1925
<>
26+
<Paywall state={state} hidePaywall={hidePaywall} />
27+
2028
<LoginModal isShowing={isShowingLogin} onClose={() => setIsShowingLogin(false)} />
2129

2230
<AIChat examples={examples} onApiError={handleApiError} />

src/app/chat/page.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,24 @@ import { auth } from '@/lib/auth';
33
import { headers } from 'next/headers';
44
import { examples } from '@/lib/ai';
55
import AiApp from './ai-app';
6+
import { freemius } from '@/lib/freemius';
7+
import FSCheckoutProvider from '@/components/fs-checkout';
68

79
export default async function Dashboard() {
810
const session = await auth.api.getSession({
911
headers: await headers(),
1012
});
1113

14+
const checkout = await freemius.checkout.create({
15+
user: session?.user,
16+
isSandbox: process.env.NODE_ENV !== 'production',
17+
});
18+
1219
return (
1320
<AppMain title="New Chat" isLoggedIn={!!session}>
14-
<AiApp examples={examples} />
21+
<FSCheckoutProvider checkout={checkout.serialize()}>
22+
<AiApp examples={examples} />
23+
</FSCheckoutProvider>
1524
</AppMain>
1625
);
1726
}

src/components/fs-checkout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { CheckoutProvider } from '@/react-starter/components/checkout-provider';
44
import { CheckoutSerialized } from '@freemius/sdk';
5-
import { IconCircleCheck, IconAlertCircle } from '@tabler/icons-react';
5+
import { IconCircleCheck } from '@tabler/icons-react';
66
import * as React from 'react';
77
import { toast } from 'sonner';
88

0 commit comments

Comments
 (0)