-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Prem Prakash Sharma
committed
Jul 29, 2024
1 parent
9ac115e
commit 1af0758
Showing
13 changed files
with
540 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
|
||
|
||
# Stripe | ||
STRIPE_SECRET_KEY={{STRIPE_SECRET_KEY}} | ||
STRIPE_PUBLIC_KEY={{STRIPE_PUBLIC_KEY}} | ||
|
||
# Razorpay | ||
RAZORPAY_KEY_ID={{RAZORPAY_KEY_ID}} | ||
RAZORPAY_KEY_SECRET={{RAZORPAY_KEY_SECRET}} | ||
|
||
# Paypal | ||
PAYPAL_CLIENT_ID={{PAYPAL_CLIENT_ID}} | ||
PAYPAL_CLIENT_SECRET={{PAYPAL_CLIENT_SECRET}} | ||
|
||
# Coinbase | ||
COINBASE_API_KEY={{COINBASE_API_KEY}} | ||
COINBASE_API_SECRET={{COINBASE_API_SECRET}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
"use server"; | ||
import Stripe from "stripe"; | ||
import products from "@/data/products.json"; | ||
import { NextResponse } from "next/server"; | ||
|
||
interface CreateCheckoutSessionInput { | ||
productId: number; | ||
quantity: number; | ||
} | ||
|
||
export async function createCheckoutSession({ productId, quantity }: CreateCheckoutSessionInput) { | ||
if (!productId || !quantity) { | ||
return { error: "Invalid input" }; | ||
} | ||
|
||
const product = products.find((product) => product.id === productId); | ||
|
||
if (!product) { | ||
return { error: "Product not found" }; | ||
} | ||
|
||
try { | ||
const apiKey = process.env.STRIPE_SECRET_KEY; | ||
|
||
if (!apiKey) { | ||
throw new Error("Stripe secret key is missing!"); | ||
} | ||
|
||
const stripe = new Stripe(apiKey); | ||
|
||
const session = await stripe.checkout.sessions.create({ | ||
mode: "payment", | ||
success_url: `${process.env.NEXT_PUBLIC_BASE_URL}/status?payment=success`, | ||
cancel_url: `${process.env.NEXT_PUBLIC_BASE_URL}/status?payment=failed`, | ||
line_items: [ | ||
{ | ||
price_data: { | ||
currency: "USD", | ||
product_data: { | ||
name: product.name, | ||
images: [`${process.env.NEXT_PUBLIC_BASE_URL}${product.image}`], | ||
}, | ||
unit_amount: (product.price - product.price * (product.discount / 100)) * 100, // Stripe requires the price in cents so we multiply by 100 | ||
}, | ||
quantity, | ||
}, | ||
], | ||
}); | ||
|
||
// You do database operations here to save the session.id and other details | ||
console.log("Session created", session.id); | ||
return { sessionId: session.id }; | ||
} catch (error) { | ||
console.log("Error creating checkout session", error); | ||
return { error: "Error creating checkout session" }; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import Link from "next/link"; | ||
import { redirect } from "next/navigation"; | ||
|
||
export default function Status({ searchParams }: { searchParams: { payment: string } }) { | ||
const payment = searchParams.payment; | ||
|
||
if (payment == "failed") { | ||
return ( | ||
<div className="h-screen w-full flex items-center justify-center"> | ||
<div className=" bg-gray-200 dark:bg-gray-950 shadow-md rounded-lg p-6 md:mx-auto"> | ||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
className=" h-20 w-20 text-red-600 mx-auto my-6" | ||
fill="currentColor" | ||
viewBox="0 -8 528 528" | ||
> | ||
<title>fail</title> | ||
<path d="M264 456Q210 456 164 429 118 402 91 356 64 310 64 256 64 202 91 156 118 110 164 83 210 56 264 56 318 56 364 83 410 110 437 156 464 202 464 256 464 310 437 356 410 402 364 429 318 456 264 456ZM264 288L328 352 360 320 296 256 360 192 328 160 264 224 200 160 168 192 232 256 168 320 200 352 264 288Z" /> | ||
</svg> | ||
|
||
<div className="text-center"> | ||
<h3 className="md:text-2xl text-base text-gray-900 dark:text-gray-200 font-semibold text-center"> | ||
Payment Failed! | ||
</h3> | ||
<p className="text-gray-500 my-2">We are sorry, but your payment did not go through.</p> | ||
<p>Please try again later.</p> | ||
<div className="py-10 text-center"> | ||
<Link | ||
href="/" | ||
className="px-12 bg-blue-600 rounded-full shadow-md hover:bg-blue-500 text-white font-semibold py-3" | ||
> | ||
GO BACK | ||
</Link> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
if (payment == "success") { | ||
return ( | ||
<div className="h-screen w-full flex items-center justify-center"> | ||
<div className=" bg-gray-200 dark:bg-gray-950 shadow-md rounded-lg p-6 md:mx-auto"> | ||
<svg viewBox="0 0 24 24" className="text-green-600 w-16 h-16 mx-auto my-6"> | ||
<path | ||
fill="currentColor" | ||
d="M12,0A12,12,0,1,0,24,12,12.014,12.014,0,0,0,12,0Zm6.927,8.2-6.845,9.289a1.011,1.011,0,0,1-1.43.188L5.764,13.769a1,1,0,1,1,1.25-1.562l4.076,3.261,6.227-8.451A1,1,0,1,1,18.927,8.2Z" | ||
></path> | ||
</svg> | ||
<div className="text-center"> | ||
<h3 className="md:text-2xl text-base text-gray-900 dark:text-gray-200 font-semibold text-center"> | ||
Payment Done! | ||
</h3> | ||
<p className="text-gray-500 my-2"> | ||
Thank you for completing your secure online payment. | ||
</p> | ||
<p>Have a great day!</p> | ||
<div className="py-10 text-center"> | ||
<Link | ||
href="/" | ||
className="px-12 bg-blue-600 rounded-full shadow-md hover:bg-blue-500 text-white font-semibold py-3" | ||
> | ||
GO BACK | ||
</Link> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
return redirect("/"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,108 @@ | ||
"use client"; | ||
import Image from "next/image"; | ||
import products from "@/data/products.json"; | ||
import { useTransition } from "react"; | ||
import { createCheckoutSession } from "@/actions/stripe"; | ||
import { loadStripe } from "@stripe/stripe-js"; | ||
import { toast } from "react-toastify"; | ||
|
||
export default function Stripe() { | ||
const [loading, startTransition] = useTransition(); | ||
|
||
const product = products[0]; | ||
|
||
function handleBuy() { | ||
startTransition(async () => { | ||
const result = await createCheckoutSession({ productId: product.id, quantity: 1 }); | ||
|
||
if (!result || result.error || !result.sessionId) { | ||
toast.error("Error creating checkout session"); | ||
return; | ||
} | ||
|
||
const publishableKey = process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY; | ||
if (!publishableKey) { | ||
toast.error("Stripe public key is missing!"); | ||
return; | ||
} | ||
const stripe = await loadStripe(publishableKey); | ||
|
||
if (!stripe) { | ||
toast.error("Error loading Stripe"); | ||
return; | ||
} | ||
|
||
const { error } = await stripe.redirectToCheckout({ | ||
sessionId: result.sessionId, | ||
}); | ||
|
||
if (error) { | ||
toast.error("Error redirecting to checkout"); | ||
} | ||
}); | ||
} | ||
|
||
return ( | ||
<div> | ||
<h1>Stripe</h1> | ||
<div className="h-screen w-full flex justify-center items-center"> | ||
<div className="flex w-full max-w-xs flex-col overflow-hidden rounded-lg bg-white dark:bg-gray-950 shadow-md"> | ||
<div className="relative m-2 flex h-60 overflow-hidden rounded-xl"> | ||
<Image | ||
height={500} | ||
width={500} | ||
className="object-cover" | ||
src={product.image} | ||
alt="product image" | ||
/> | ||
<span className="absolute top-0 left-0 m-2 rounded-full bg-black px-2 text-center text-sm font-medium text-white"> | ||
{product.discount}% OFF | ||
</span> | ||
</div> | ||
<div className="mt-4 px-5 pb-5"> | ||
<a href="#"> | ||
<h5 className="text-xl tracking-tight text-slate-900 dark:text-gray-200"> | ||
{product.name} | ||
</h5> | ||
</a> | ||
<div className="mt-2 mb-5 flex items-center justify-between"> | ||
<p> | ||
<span className="text-3xl font-bold text-slate-900 dark:text-gray-200"> | ||
${product.price - product.price * (product.discount / 100)} | ||
</span> | ||
<span className="text-sm text-slate-900 dark:text-gray-200 line-through"> | ||
${product.price} | ||
</span> | ||
</p> | ||
<div className="flex items-center"> | ||
{[...Array(product.rating)].map((_, index) => ( | ||
<svg | ||
key={index} | ||
aria-hidden="true" | ||
className="h-5 w-5 text-yellow-500 " | ||
fill="currentColor" | ||
viewBox="0 0 20 20" | ||
xmlns="http://www.w3.org/2000/svg" | ||
> | ||
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" /> | ||
</svg> | ||
))} | ||
|
||
<span | ||
className="mr-2 ml-3 rounded bg-yellow-500 text-white | ||
px-2.5 py-0.5 text-xs font-semibold" | ||
> | ||
{product.rating}.0 | ||
</span> | ||
</div> | ||
</div> | ||
<button | ||
disabled={loading} | ||
onClick={handleBuy} | ||
className="w-full rounded-md bg-slate-900 px-5 py-2.5 text-center text-sm font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-blue-300" | ||
> | ||
{loading ? "Loading..." : "Hire Me"} | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} |
Oops, something went wrong.