diff --git a/frontend/src/app/api/create-payment-intent/route.ts b/frontend/src/app/api/create-payment-intent/route.ts new file mode 100644 index 0000000..642f7fa --- /dev/null +++ b/frontend/src/app/api/create-payment-intent/route.ts @@ -0,0 +1,33 @@ +import { NextResponse } from 'next/server'; +import Stripe from 'stripe'; + +const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { + apiVersion: '2024-11-20.acacia' +}); + +export async function POST(req: Request) { + try { + const body = await req.json(); + const { items } = body; + + const paymentIntent = await stripe.paymentIntents.create({ + amount: Math.round(items.reduce((sum: number, item: any) => + sum + (item.price * item.quantity), 0) * 100), + currency: 'usd', + payment_method_types: ['card', 'paypal', 'apple_pay', 'google_pay'], + automatic_payment_methods: { + enabled: false + }, + }); + + return NextResponse.json({ + clientSecret: paymentIntent.client_secret + }); + } catch (error) { + console.error('Stripe error:', error); + return NextResponse.json( + { error: 'Error creating payment intent' }, + { status: 500 } + ); + } +} \ No newline at end of file diff --git a/frontend/src/app/checkout/page.tsx b/frontend/src/app/checkout/page.tsx new file mode 100644 index 0000000..2b41d2d --- /dev/null +++ b/frontend/src/app/checkout/page.tsx @@ -0,0 +1,12 @@ +'use client'; + +import StripeCheckout from '@/lib/stripe/checkout'; + +export default function CheckoutPage() { + return ( +
+

Checkout

+ +
+ ); +} \ No newline at end of file diff --git a/frontend/src/app/routes/cart/page.tsx b/frontend/src/app/routes/cart/page.tsx deleted file mode 100644 index b921ab0..0000000 --- a/frontend/src/app/routes/cart/page.tsx +++ /dev/null @@ -1,21 +0,0 @@ -// src/app/(routes)/cart/page.tsx -'use client'; - -import { useCartStore } from '@/lib/store/cart-store'; -import StripeCheckout from '@/lib/stripe/checkout'; - -export default function CartPage() { - const { items, getTotalPrice } = useCartStore(); - - return ( -
-

Your Cart

- {items.map(item => ( -
- {item.title} - ${item.price} x {item.quantity} -
- ))} - -
- ); -} \ No newline at end of file diff --git a/frontend/src/app/routes/checkout/page.tsx b/frontend/src/app/routes/checkout/page.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/src/app/routes/payment-confirmation/page.tsx b/frontend/src/app/routes/payment-confirmation/page.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/src/components/CartTab/CartSidebar.tsx b/frontend/src/components/CartTab/CartSidebar.tsx index f8887a9..b0a6d00 100644 --- a/frontend/src/components/CartTab/CartSidebar.tsx +++ b/frontend/src/components/CartTab/CartSidebar.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { motion } from 'framer-motion'; import type { CartItem } from '@/lib/store/cart-store'; import Image from 'next/image'; +import { useRouter } from 'next/navigation'; interface CartSidebarProps { items: CartItem[]; @@ -18,6 +19,8 @@ const CartSidebar: React.FC = ({ updateQuantity, removeItem, }) => { + const router = useRouter(); + // Count occurrences of each item ID const itemCounts = items.reduce((acc, item) => { acc[item.id] = (acc[item.id] || 0) + 1; @@ -43,6 +46,32 @@ const CartSidebar: React.FC = ({ removeItem(itemId); } + const handleCheckout = async () => { + try { + // Create payment intent + const response = await fetch('/api/create-payment-intent', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ items: uniqueItems }), + }); + + if (!response.ok) { + throw new Error('Network response was not ok'); + } + + // Close the cart sidebar + onClose(); + + // Redirect to checkout page + router.push('/checkout'); + } catch (error) { + console.error('Error initiating checkout:', error); + // You might want to show an error message to the user here + } + }; + return ( <> {/* Overlay */} @@ -140,7 +169,9 @@ const CartSidebar: React.FC = ({ ${total.toFixed(2)} diff --git a/frontend/src/lib/stripe/checkout-form.tsx b/frontend/src/lib/stripe/checkout-form.tsx index fe9eb1c..4514548 100644 --- a/frontend/src/lib/stripe/checkout-form.tsx +++ b/frontend/src/lib/stripe/checkout-form.tsx @@ -4,10 +4,12 @@ import React, { useState, FormEvent } from 'react'; import { PaymentElement, useStripe, - useElements + useElements, + AddressElement } from '@stripe/react-stripe-js'; import { StripePaymentElementOptions } from '@stripe/stripe-js'; import { useCartStore } from '@/lib/store/cart-store'; +import Image from 'next/image'; export default function CheckoutForm() { const stripe = useStripe(); @@ -15,7 +17,7 @@ export default function CheckoutForm() { const [isProcessing, setIsProcessing] = useState(false); const [message, setMessage] = useState(null); - const { clearCart } = useCartStore(); + const { clearCart, items, getTotalPrice } = useCartStore(); const handleSubmit = async (event: FormEvent) => { event.preventDefault(); @@ -50,24 +52,139 @@ export default function CheckoutForm() { }; const paymentElementOptions: StripePaymentElementOptions = { - layout: 'tabs', + layout: { + type: 'tabs', + defaultCollapsed: false, + radios: false, + spacedAccordionItems: false + }, + paymentMethodOrder: [ 'card', 'apple_pay', 'google_pay', 'paypal', 'amazon_pay'] }; + const total = getTotalPrice({ items }); + const shipping = 5.99; + const taxRate = 0.0825; + const subtotal = total; + const tax = subtotal * taxRate; + const finalTotal = subtotal + shipping + tax; + return ( -
- - - {message && ( -
- {message} +
+ +
+
+
+ {/* Left Column - Payment Method & Order Summary */} +
+ {/* Payment Method */} +
+

Payment Method

+ +
+ +
+

Order Summary

+
+ {items.map((item) => ( +
+
+ {item.title} +
+
+

{item.title}

+

Quantity: {item.quantity}

+
+
+

${(item.price * item.quantity).toFixed(2)}

+

${item.price.toFixed(2)} each

+
+
+ ))} +
+
+ Total + ${total.toFixed(2)} +
+
+
+
+ + {/* Error Message */} + {message && ( +
+ {message} +
+ )} +
+ + {/* Right Column - Shipping Address & Total Breakdown */} +
+ {/* Shipping Address */} +
+

Shipping Address

+ +
+ + {/* Order Total Breakdown */} +
+
+
+ Subtotal + ${subtotal.toFixed(2)} +
+
+ Shipping + ${shipping.toFixed(2)} +
+
+ Tax + ${tax.toFixed(2)} +
+
+ Total + ${finalTotal.toFixed(2)} +
+
+
+ + {/* Submit Button */} + +
+
+
- )} - + +
); } \ No newline at end of file