Skip to content

Commit

Permalink
add gig page and stripe
Browse files Browse the repository at this point in the history
  • Loading branch information
musiur committed Jan 23, 2025
1 parent be77d59 commit d88232d
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 0 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"react-whatsapp": "^0.3.0",
"sass": "^1.76.0",
"sharp": "^0.33.4",
"stripe": "^17.5.0",
"swiper": "^11.1.1",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
Expand Down
9 changes: 9 additions & 0 deletions src/app/api/stripe/create/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

import { createStripeCheckout } from '@/lib/stripe/stripe';
import { NextResponse } from 'next/server';

export async function POST(request: Request) {
const { gigId } = await request.json();
const url = await createStripeCheckout(gigId);
return NextResponse.json({ url });
}
23 changes: 23 additions & 0 deletions src/app/api/stripe/verify/page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { NextResponse } from 'next/server';
import { stripe } from '@/lib/stripe/stripe';

export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const sessionId = searchParams.get('session_id');

if (!sessionId) {
return NextResponse.json({ success: false }, { status: 400 });
}

try {
const session = await stripe.checkout.sessions.retrieve(sessionId);

if (session.payment_status === 'paid') {
return NextResponse.json({ success: true, session });
} else {
return NextResponse.json({ success: false }, { status: 400 });
}
} catch (error) {
return NextResponse.json({ success: false }, { status: 500 });
}
}
Empty file.
12 changes: 12 additions & 0 deletions src/app/gigs/_utils/components/data/gig-list-data.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { T__Gig } from "../types/gigs-data-types";

export const GigsData: T__Gig[] = [
{
id: 1,
title: "Web Development",
description: "I will build a modern website for your business",
price: 50,
fiverrLink: "https://fiverr.com/your-gig",
image: "/web-dev.jpg",
},
];
54 changes: 54 additions & 0 deletions src/app/gigs/_utils/components/gig-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"use client"

import Link from "next/link";
import { T__Gig } from "../types/gigs-data-types";

export default function GigCard({ gig }: { gig: T__Gig }) {
return (
<div className="border rounded-lg overflow-hidden shadow-lg">
<img
src={gig.image}
alt={gig.title}
className="w-full h-48 object-cover"
/>
<div className="p-4">
<h2 className="text-xl font-semibold mb-2">{gig.title}</h2>
<p className="text-gray-600 mb-4">{gig.description}</p>
<div className="flex justify-between items-center">
<span className="text-lg font-bold">${gig.price}</span>
<div className="space-x-2">
<Link
href={gig.fiverrLink}
className="bg-green-500 text-white px-4 py-2 rounded"
>
Buy on Fiverr
</Link>
<button
onClick={() => handleStripePurchase(gig.id)}
className="bg-blue-500 text-white px-4 py-2 rounded"
>
Buy with Stripe
</button>
</div>
</div>
</div>
</div>
);
}

async function handleStripePurchase(gigId: number) {
try {
const response = await fetch("/api/create", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ gigId }),
});

const { url } = await response.json();
window.location.href = url;
} catch (error) {
console.error("Error:", error);
}
}
13 changes: 13 additions & 0 deletions src/app/gigs/_utils/components/gig-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { T__Gig } from "../types/gigs-data-types";
import { GigsData } from "./data/gig-list-data";
import GigCard from "./gig-card";

export default function GigList() {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{GigsData.map((gig: T__Gig) => (
<GigCard key={gig.id} gig={gig} />
))}
</div>
);
}
8 changes: 8 additions & 0 deletions src/app/gigs/_utils/types/gigs-data-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type T__Gig = {
id: number,
title: string,
description: string,
price: number,
fiverrLink: string,
image: string,
}
10 changes: 10 additions & 0 deletions src/app/gigs/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import GigList from './_utils/components/gig-list';

export default function GigsPage() {
return (
<div className="container mx-auto px-4 py-8">
<h1 className="text-3xl font-bold mb-8">My Fiverr Gigs</h1>
<GigList />
</div>
);
}
54 changes: 54 additions & 0 deletions src/app/gigs/payment-verify/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"use client";

import { useEffect, useState } from "react";
import { useSearchParams } from "next/navigation";

export default function PaymentVerificationPage() {
const searchParams = useSearchParams();
const [paymentStatus, setPaymentStatus] = useState("verifying");

useEffect(() => {
const sessionId = searchParams.get("session_id");
const success = searchParams.get("success");

if (sessionId && success) {
verifyPayment(sessionId);
} else {
setPaymentStatus("invalid");
}
}, [searchParams]);

async function verifyPayment(sessionId: string) {
try {
const response = await fetch(
`/api/verify-payment?session_id=${sessionId}`
);
const data = await response.json();

if (data.success) {
setPaymentStatus("success");
// You can show order details here
} else {
setPaymentStatus("failed");
}
} catch (error) {
setPaymentStatus("error");
}
}

return (
<div className="container mx-auto px-4 py-8">
{paymentStatus === "verifying" && <p>Verifying payment...</p>}
{paymentStatus === "success" && <p>Payment successful!</p>}
{paymentStatus === "failed" && (
<p>Payment verification failed. Please contact support.</p>
)}
{paymentStatus === "invalid" && (
<p>Invalid session. Please contact support.</p>
)}
{paymentStatus === "error" && (
<p>Error verifying payment. Please contact support.</p>
)}
</div>
);
}
33 changes: 33 additions & 0 deletions src/lib/stripe/stripe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { GigsData } from '@/app/gigs/_utils/components/data/gig-list-data';
import Stripe from 'stripe';

export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function createStripeCheckout(gigId: number) {
const gig = GigsData.find(g => g.id === gigId);
const metadata = {
userId: "test_user_43634563",
gigId: gigId.toString(),
}

const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [{
price_data: {
currency: 'usd',
product_data: {
name: gig.title,
},
unit_amount: gig.price * 100,
},
quantity: 1,
}],
mode: 'payment',
metadata,
payment_intent_data: { metadata },
success_url: `${process.env.NEXT_PUBLIC_URL}/gigs/payment-verify?success=true`,
cancel_url: `${process.env.NEXT_PUBLIC_URL}/gigs/payment-verify?success=false`,
});

return session.url;
}

0 comments on commit d88232d

Please sign in to comment.