Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Checkout and Stripe setup #11

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# <img src="public/assets/images/logo.svg" alt="logo" />

Build an event organization web app like Eventbrite or Meetup with authentication, event management, search, filtering, categories, checkout, and payments using Next JS 14, Tailwind CSS, Shadcn, React Hook Form, Zod, Uploadthing, React-Datepicker, Mongoose, Clerk, and Stripe.

`ngrok tunnel --label edge=edghts_2jQbAk4QzC90ArEGm4eYkVIhI1v http://localhost:3000`
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question (documentation): Check if the ngrok label is sensitive.

Please verify if the label edge=edghts_2jQbAk4QzC90ArEGm4eYkVIhI1v is sensitive information. If it is, consider removing it or replacing it with a placeholder.

5 changes: 4 additions & 1 deletion app/(root)/events/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// import CheckoutButton from '@/components/shared/CheckoutButton';
import CheckoutButton from "@/components/shared/CheckoutButton";
import Collection from "@/components/shared/Collection";
import {
getEventById,
Expand Down Expand Up @@ -51,7 +52,9 @@ const EventDetails = async ({
</p>
</div>
</div>
CheckoutButton

<CheckoutButton event={event} />

<div className="flex flex-col gap-5">
<div className="flex gap-2 md:gap-3">
<Image
Expand Down
45 changes: 45 additions & 0 deletions components/shared/Checkout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, { useEffect } from "react";
import { loadStripe } from "@stripe/stripe-js";

import { IEvent } from "@/lib/database/models/event.model";
import { Button } from "../ui/button";
import { checkoutOrder } from "@/lib/actions/order.actions";

loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);

const Checkout = ({ event, userId }: { event: IEvent; userId: string }) => {
useEffect(() => {
const query = new URLSearchParams(window.location.search);
if (query.get("success")) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Consider using a more robust method for order confirmation

Using console.log for order confirmation and cancellation may not be ideal in a production environment. Consider implementing a more user-friendly way of communicating the order status, such as displaying a message in the UI.

Suggested change
if (query.get("success")) {
if (query.get("success")) {
alert("Order placed! You will receive an email confirmation.");

console.log("Order placed! You will receive an email confirmation.");
}

if (query.get("canceled")) {
console.log(
"Order canceled -- continue to shop around and checkout when you’re ready.",
);
}
}, []);

const onCheckout = async () => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Consider adding error handling to the onCheckout function

Asynchronous operations, especially in checkout processes, should have proper error handling. Consider wrapping the checkoutOrder call in a try-catch block to handle potential errors gracefully.

const order = {
eventTitle: event.title,
eventId: event._id,
price: event.price,
isFree: event.isFree,
buyerId: userId,
};

await checkoutOrder(order);
};

return (
<form action={onCheckout} method="post">
<Button type="submit" role="link" size="lg" className="button sm:w-fit">
{event.isFree ? "Get Tickets" : "Buy Tickets"}
</Button>
</form>
);
};

export default Checkout;
29 changes: 29 additions & 0 deletions components/shared/CheckoutButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use client";

import { SignedIn, SignedOut, useUser } from "@clerk/nextjs";
import React from "react";
import { Button } from "../ui/button";
import Link from "next/link";
import Checkout from "./Checkout";
import { IEvent } from "@/lib/database/models/event.model";

const CheckoutButton = ({ event }: { event: IEvent }) => {
const { user } = useUser();
const userId = user?.publicMetadata.userId as string;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Consider adding a comment explaining the use of publicMetadata

While using publicMetadata to get the userId is likely intentional, it might be helpful to add a brief comment explaining why this approach is used, for the benefit of other developers who might work on this code in the future.

Suggested change
const userId = user?.publicMetadata.userId as string;
// Using publicMetadata to retrieve userId for specific business logic
const userId = user?.publicMetadata.userId as string;


return (
<div className="flex items-center gap-3">
<SignedOut>
<Button asChild size="lg" className="button rounded-full">
<Link href="/sign-in">Get Tickets</Link>
</Button>
</SignedOut>

<SignedIn>
<Checkout event={event} userId={userId} />
</SignedIn>
</div>
);
};

export default CheckoutButton;