diff --git a/src/hooks/useCheckout.ts b/src/hooks/useCheckout.ts index cd8b645..961228f 100644 --- a/src/hooks/useCheckout.ts +++ b/src/hooks/useCheckout.ts @@ -1,75 +1,117 @@ -import { useState, useCallback } from "react"; +"use client"; -interface CheckoutItem { - id: string; - name: string; - price: number; - quantity: number; -} +import { useState } from "react"; +import { Product } from "@/lib/api/product"; -interface CheckoutState { - items: CheckoutItem[]; - total: number; - loading: boolean; - error: string | null; -} - -const useCheckout = () => { - const [state, setState] = useState({ - items: [], - total: 0, - loading: false, - error: null, - }); - - const addItem = useCallback((item: CheckoutItem) => { - setState((prev) => ({ - ...prev, - items: [...prev.items, item], - total: prev.total + item.price * item.quantity, - })); - }, []); - - const removeItem = useCallback((itemId: string) => { - setState((prev) => { - const item = prev.items.find((i) => i.id === itemId); - return { - ...prev, - items: prev.items.filter((i) => i.id !== itemId), - total: prev.total - (item ? item.price * item.quantity : 0), - }; - }); - }, []); - - const clearCart = useCallback(() => { - setState({ - items: [], - total: 0, - loading: false, - error: null, - }); - }, []); - - const checkout = useCallback(async () => { - setState((prev) => ({ ...prev, loading: true, error: null })); - try { - console.log("Processing checkout with items:", state.items); - setState((prev) => ({ ...prev, loading: false })); - } catch (err) { - setState((prev) => ({ - ...prev, - loading: false, - error: err instanceof Error ? err.message : "Checkout failed"})); - } - }, [state.items]); - - return { - ...state, - addItem, - removeItem, - clearCart, - 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 default useCheckout; +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