diff --git a/src/hooks/useCheckout.ts b/src/hooks/useCheckout.ts index c9aa614..961228f 100644 --- a/src/hooks/useCheckout.ts +++ b/src/hooks/useCheckout.ts @@ -1,62 +1,117 @@ +"use client"; + import { useState } from "react"; +import { Product } from "@/lib/api/product"; -export interface CartItem { - id: string; - name: string; - price: number; - quantity: number; -} - -export const useCheckout = () => { - const [cartItems, setCartItems] = useState([]); - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(null); - - const addToCart = (item: CartItem) => { - setCartItems((prev) => { - const existing = prev.find((i) => i.id === item.id); - if (existing) { - return prev.map((i) => (i.id === item.id ? { ...i, quantity: i.quantity + item.quantity } : i)); - } - return [...prev, item]; - }); - }; - - const removeFromCart = (id: string) => { - setCartItems((prev) => prev.filter((item) => item.id !== id)); - }; - - const updateQuantity = (id: string, quantity: number) => { - setCartItems((prev) => - prev.map((item) => (item.id === id ? { ...item, quantity: Math.max(1, quantity) } : item)) - ); - }; - - const getTotal = () => { - return cartItems.reduce((total, item) => total + item.price * item.quantity, 0).toFixed(2); - }; - - const checkout = async () => { - setIsLoading(true); - try { - // Simulate checkout process - await new Promise((resolve) => setTimeout(resolve, 1000)); - setCartItems([]); - } catch (err) { - setError(err instanceof Error ? err.message : "Checkout failed"); - } finally { - setIsLoading(false); - } - }; - - return { - cartItems, - isLoading, - error, - addToCart, - removeFromCart, - updateQuantity, - getTotal, - checkout - }; +export type CheckoutItem = { + productId: string; + quantity: number; + imageSrc?: string; + imageAlt?: string; + metadata?: { + brand?: string; + variant?: string; + rating?: number; + reviewCount?: string; + [key: string]: string | number | undefined; + }; }; + +export type CheckoutResult = { + success: boolean; + url?: string; + error?: string; +}; + +export function useCheckout() { + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + + const checkout = async (items: CheckoutItem[], options?: { successUrl?: string; cancelUrl?: string }): Promise => { + const apiUrl = process.env.NEXT_PUBLIC_API_URL; + const projectId = process.env.NEXT_PUBLIC_PROJECT_ID; + + if (!apiUrl || !projectId) { + const errorMsg = "NEXT_PUBLIC_API_URL or NEXT_PUBLIC_PROJECT_ID not configured"; + setError(errorMsg); + return { success: false, error: errorMsg }; + } + + setIsLoading(true); + setError(null); + + try { + + const response = await fetch(`${apiUrl}/stripe/project/checkout-session`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + projectId, + items, + successUrl: options?.successUrl || window.location.href, + cancelUrl: options?.cancelUrl || window.location.href, + }), + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + const errorMsg = errorData.message || `Request failed with status ${response.status}`; + setError(errorMsg); + return { success: false, error: errorMsg }; + } + + const data = await response.json(); + + if (data.data.url) { + window.location.href = data.data.url; + } + + return { success: true, url: data.data.url }; + } catch (err) { + const errorMsg = err instanceof Error ? err.message : "Failed to create checkout session"; + setError(errorMsg); + return { success: false, error: errorMsg }; + } finally { + setIsLoading(false); + } + }; + + const buyNow = async (product: Product | string, quantity: number = 1): Promise => { + const successUrl = new URL(window.location.href); + successUrl.searchParams.set("success", "true"); + + if (typeof product === "string") { + return checkout([{ productId: product, quantity }], { successUrl: successUrl.toString() }); + } + + let metadata: CheckoutItem["metadata"] = {}; + + if (product.metadata && Object.keys(product.metadata).length > 0) { + const { imageSrc, imageAlt, images, ...restMetadata } = product.metadata; + metadata = restMetadata; + } else { + if (product.brand) metadata.brand = product.brand; + if (product.variant) metadata.variant = product.variant; + if (product.rating !== undefined) metadata.rating = product.rating; + if (product.reviewCount) metadata.reviewCount = product.reviewCount; + } + + return checkout([{ + productId: product.id, + quantity, + imageSrc: product.imageSrc, + imageAlt: product.imageAlt, + metadata: Object.keys(metadata).length > 0 ? metadata : undefined, + }], { successUrl: successUrl.toString() }); + }; + + return { + checkout, + buyNow, + isLoading, + error, + clearError: () => setError(null), + }; +} \ No newline at end of file