diff --git a/src/hooks/useCheckout.ts b/src/hooks/useCheckout.ts index 469f22e..961228f 100644 --- a/src/hooks/useCheckout.ts +++ b/src/hooks/useCheckout.ts @@ -1,45 +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"; -export const useCheckout = () => { - const [items, setItems] = useState([]); - const [total, setTotal] = useState(0); - - const addItem = useCallback( - (item: CheckoutItem) => { - setItems((prev) => [...prev, item]); - setTotal((prev) => prev + item.price * item.quantity); - }, - [] - ); - - const removeItem = useCallback( - (id: string) => { - const item = items.find((i) => i.id === id); - if (item) { - setTotal((prev) => prev - item.price * item.quantity); - setItems((prev) => prev.filter((i) => i.id !== id)); - } - }, - [items] - ); - - const clearCart = useCallback(() => { - setItems([]); - setTotal(0); - }, []); - - return { - items, - total, - addItem, - removeItem, - clearCart, - }; +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