Skip to content

Commit

Permalink
Merge pull request #45 from dallen4/feat/premium
Browse files Browse the repository at this point in the history
Feature: Premium License
  • Loading branch information
dallen4 authored Aug 1, 2023
2 parents b3775cf + 62f82b2 commit 7b1fb0f
Show file tree
Hide file tree
Showing 8 changed files with 546 additions and 3 deletions.
37 changes: 37 additions & 0 deletions web/api/auth0.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ManagementClient } from 'auth0';
import { UserMetadata } from 'types/users';

const { host } = new URL(process.env.AUTH0_ISSUER_BASE_URL!);

export const auth0 = new ManagementClient<{ [key: string]: any }, UserMetadata>(
{
domain: host,
token: process.env.AUTH0_TOKEN!,
},
);

export const getUserById = async (id: string) => {
const user = await auth0.getUser({ id });

return {
username: user.nickname,
email: user.email,
metadata: user.user_metadata,
};
};

export const getUserIdsByEmail = async (email: string) => {
const users = await auth0.getUsersByEmail(email);

return users.map((user) => user.user_id!);
};

export const updateUser = async (id: string, data: UserMetadata) => {
const updatedUser = await auth0.updateUserMetadata({ id }, data);

return {
username: updatedUser.nickname,
email: updatedUser.email,
metadata: updatedUser.user_metadata,
};
};
28 changes: 28 additions & 0 deletions web/api/stripe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { NextApiRequest } from 'next';
import Stripe from 'stripe';

const client = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2022-11-15',
});

export function buildEvent(request: NextApiRequest) {
const sig = request.headers['stripe-signature']!;

const event = client.webhooks.constructEvent(
request.body,
sig,
process.env.STRIPE_WEBHOOK_SECRET!,
);

return event;
}

export async function getEmailForCheckout(id: string) {
const session = await client.checkout.sessions.retrieve(id, {
expand: ['line_items'],
});

// session.line_items!.data[0]

return session.customer_email!;
}
5 changes: 5 additions & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@
"@mantine/next": "^5.2.3",
"@mantine/notifications": "^5.2.3",
"@sentry/nextjs": "^7.46.0",
"@stripe/react-stripe-js": "^2.1.1",
"@stripe/stripe-js": "^1.54.1",
"@tabler/icons": "^1.119.0",
"@vercel/analytics": "^0.1.8",
"@xstate/react": "^3.0.1",
"auth0": "^3.5.0",
"cookies-next": "^2.0.4",
"cors": "^2.8.5",
"ioredis": "^5.0.4",
Expand All @@ -43,6 +46,7 @@
"react-dom": "^18.2.0",
"request-ip": "^3.3.0",
"sharp": "^0.32.0",
"stripe": "^12.13.0",
"ua-parser-js": "^1.0.32",
"unique-names-generator": "^4.7.1",
"xstate": "^4.33.4",
Expand All @@ -53,6 +57,7 @@
"@next/eslint-plugin-next": "^13.1.1",
"@playwright/test": "^1.28.1",
"@testing-library/react-hooks": "^8.0.1",
"@types/auth0": "^3.3.3",
"@types/cors": "^2.8.13",
"@types/js-cookie": "^3.0.2",
"@types/node-mailjet": "^3.3.8",
Expand Down
23 changes: 23 additions & 0 deletions web/pages/api/me.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { cors } from 'api/middleware/cors';
import { runMiddleware } from 'api/middleware';
import { getUserById } from 'api/auth0';
import { getSession } from '@auth0/nextjs-auth0';

export default async function me(req: NextApiRequest, res: NextApiResponse) {
await runMiddleware(req, res, cors);

if (!['GET'].includes(req.method!)) {
res.setHeader('Allow', 'GET');
res.status(405).end('Method Not Allowed');
return;
}

const session = await getSession(req, res);

if (session) {
const user = await getUserById(session!.user.sub);

return res.status(200).json(user);
} else return res.status(200);
}
31 changes: 31 additions & 0 deletions web/pages/api/stripe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { getUserIdsByEmail, updateUser } from 'api/auth0';
import { runMiddleware } from 'api/middleware';
import { cors } from 'api/middleware/cors';
import { buildEvent, getEmailForCheckout } from 'api/stripe';
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function stripeWebhook(
req: NextApiRequest,
res: NextApiResponse,
) {
await runMiddleware(req, res, cors);

const event = buildEvent(req);

if (event.type === 'checkout.session.completed') {
const sessionId = (event.data.object as any).id;
const email = await getEmailForCheckout(sessionId);

console.log(email);

const usersToUpdate = await getUserIdsByEmail(email);

const userUpdates = usersToUpdate.map((id) =>
updateUser(id, { premium: true }),
);

await Promise.all(userUpdates);
}

return;
}
14 changes: 14 additions & 0 deletions web/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { GRAB_PATH } from '@config/paths';
import { Faq } from 'molecules/Faq';
import { useMediaQuery } from '@mantine/hooks';

const paymentLink = process.env.NEXT_PUBLIC_STRIPE_LIFETIME_LICENSE_LINK!;

const Home = () => {
const router = useRouter();
const theme = useMantineTheme();
Expand Down Expand Up @@ -88,6 +90,18 @@ const Home = () => {
</Card>
</Center>
<Faq />
<Center
style={{
minHeight: '230px',
paddingTop: theme.spacing.xl,
paddingBottom: theme.spacing.md,
}}
>
<Button
onClick={() => (window.location.href = paymentLink)}
type={'button'}
>Get Premium</Button>
</Center>
</>
);
};
Expand Down
3 changes: 3 additions & 0 deletions web/types/users.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type UserMetadata = {
premium: boolean;
};
Loading

0 comments on commit 7b1fb0f

Please sign in to comment.